문법은 충분히 익혔어요. 이번 장은 실무에서 매일 마주치는 개념(설정 외부화·프로파일·트랜잭션·AOP·테스트)을 한 번에 훑고, 지금까지 배운 전부를 큰 그림으로 정리합니다. 마지막 장이에요. 끝까지 가봐요!
서버 포트, DB 주소, 외부 API 키 같은 설정 값을 자바 코드 안에 직접 써 두면(하드코딩), 값 하나 바꿀 때마다 코드를 고치고 다시 빌드해야 해요. 스프링은 이런 값들을 설정 파일에 따로 모아두고, 코드에는 "그 값을 가져다 쓴다"고만 표시하게 해줘요. 이걸 설정 외부화라고 해요.
port = 8081). 요리로 치면 레시피 종이에 "소금 5g"이라고 지워지지 않게 새겨두는 셈이라, 간을 바꾸려면 종이 자체를 다시 써야 해요. 설정 외부화는 이 값을 따로 붙이는 메모지(설정 파일)로 빼두는 일이에요.# application.properties
server.port=8081
app.welcome-message=안녕하세요, 스프링!
@Value("${키}") 문법으로 필드에 주입받아요. 코드에 값을 박지 않으니, 설정 파일만 바꾸면 코드 수정 없이 동작이 달라져요.
@Component
public class WelcomeService {
// application.properties의 값을 주입받음
@Value("${app.welcome-message}")
private String message;
@Value("${server.port}")
private int port;
}
@Value를 하나씩 붙이기 번거로워요. @ConfigurationProperties는 같은 접두어(app.*)의 값들을 객체 하나로 통째로 받아줘서 훨씬 깔끔해요.
@Component
@ConfigurationProperties(prefix = "app")
public class AppProps {
private String welcomeMessage; // app.welcome-message
// getter / setter ...
}
개발할 때 쓰는 DB와 실제 서비스(운영)에 쓰는 DB는 주소도, 계정도, 키도 다 달라요. 그렇다고 매번 값을 바꿔가며 빌드할 순 없죠. 스프링은 환경별 설정 파일을 준비해두고 어떤 걸 쓸지만 골라주면 되게 해줘요.
application-dev.properties(개발), application-prod.properties(운영)처럼요. 각 파일에 그 환경에 맞는 값만 적어둬요.
# application-dev.properties (개발)
spring.datasource.url=jdbc:h2:mem:testdb
# application-prod.properties (운영)
spring.datasource.url=jdbc:mysql://real-db:3306/app
spring.profiles.active=dev로 두면 개발용 설정이, prod로 바꾸면 운영용 설정이 적용돼요. 코드는 그대로.
@Profile("dev")가 붙은 빈은 dev 프로파일일 때만 만들어져요.
@Bean
@Profile("dev") // 개발 환경에서만 이 빈 사용
public MailSender fakeMailSender() {
return new ConsoleMailSender(); // 실제로 안 보내고 콘솔 출력
}
dev로, 서버는 prod로 같은 코드를 다른 값으로 돌릴 수 있어요.계좌 이체를 생각해봐요. "A에서 출금 → B에 입금", 이 두 작업은 반드시 함께 성공하거나 함께 실패해야 해요. 출금만 되고 입금이 실패하면 돈이 사라지니까요. 이렇게 여러 작업을 하나로 묶어 "모 아니면 도"로 처리하는 게 트랜잭션이에요. (CH08에서 본 SQL 트랜잭션과 같은 개념이에요.)
@Transactional만 붙이면, 그 안의 모든 DB 작업이 하나의 트랜잭션으로 묶여요. 끝까지 성공하면 커밋(확정), 중간에 예외가 나면 롤백(전부 되돌리기) 돼요.
@Service
public class BankService {
@Transactional // 이 메서드 전체가 하나의 트랜잭션
public void transfer(Long from, Long to, int amount) {
accountRepo.withdraw(from, amount); // 출금
accountRepo.deposit(to, amount); // 입금
// 여기서 예외가 나면? → 출금까지 전부 롤백!
}
}
개발자는 commit()이나 rollback()을 직접 호출하지 않아요. 어노테이션 하나면 스프링이 알아서 처리해줘요.
| 구분 | 핵심 로직 | 공통 관심사(AOP 대상) |
|---|---|---|
| 이체 서비스 | 출금·입금 계산 | 트랜잭션(커밋/롤백) |
| 주문 서비스 | 주문 저장 | 로깅, 실행 시간 측정 |
| 관리자 API | 데이터 조회 | 권한 체크(보안) |
System.out.println 대신 SLF4J·Logback 같은 로깅 도구를 쓰면, 상황별로 기록 양을 조절하고 파일로도 남길 수 있어서 실무에선 이걸 써요.@Transactional은 결과(트랜잭션 묶기), AOP는 그걸 가능하게 하는 원리(공통 관심사 자동 적용)예요. 로깅·보안·트랜잭션이 대표적인 AOP 활용처예요.기능을 만들 때마다 브라우저로 일일이 눌러 확인하는 건 느리고 실수하기 쉬워요. 테스트 코드를 짜두면 버튼 한 번으로 수십 개 기능을 자동 검증할 수 있어요. 스프링은 테스트를 아주 잘 지원해요.
@SpringBootTest
class BankServiceTest {
@Autowired
BankService bankService; // DI로 실제 빈 주입받음
@Test
void 이체_성공() {
bankService.transfer(1L, 2L, 1000);
// JUnit으로 결과 검증
assertThat(accountRepo.balance(1L)).isEqualTo(0);
}
}
@SpringBootTest = 앱 전체를 띄우는 통합 테스트, Mock = 일부만 떼서 하는 단위 테스트. 둘 다 JUnit으로 결과를 검증해요.여기까지 오느라 정말 고생 많았어요. 각 장에서 배운 개념들은 따로 노는 게 아니라 하나의 흐름으로 연결돼요. 큰 그림을 잡아볼게요.
application.properties/yml에 두고 @Value로 주입해요.dev/prod처럼 환경별로 설정을 갈라 쓰고, spring.profiles.active로 활성화해요.