설정 외부화 프로파일 트랜잭션·AOP 테스트 마무리 📝 문제풀이
◀ 이전 🎉 완주 · 목차
🏁 CHAPTER 10 · 고급

실무로 나가기 전, 꼭 알아둘 개념들

문법은 충분히 익혔어요. 이번 장은 실무에서 매일 마주치는 개념(설정 외부화·프로파일·트랜잭션·AOP·테스트)을 한 번에 훑고, 지금까지 배운 전부를 큰 그림으로 정리합니다. 마지막 장이에요. 끝까지 가봐요!

🎯 이 장을 끝내면
⚙️
설정 외부화
값은 코드가 아니라 "설정 파일"에
포트·DB 주소·API 키를 코드에 박으면 안 되는 이유부터.

서버 포트, DB 주소, 외부 API 키 같은 설정 값을 자바 코드 안에 직접 써 두면(하드코딩), 값 하나 바꿀 때마다 코드를 고치고 다시 빌드해야 해요. 스프링은 이런 값들을 설정 파일에 따로 모아두고, 코드에는 "그 값을 가져다 쓴다"고만 표시하게 해줘요. 이걸 설정 외부화라고 해요.

🔤
하드코딩(hard-coding) — 바뀔 수 있는 값을 코드 안에 그대로 박아 넣는 것이에요(예: port = 8081). 요리로 치면 레시피 종이에 "소금 5g"이라고 지워지지 않게 새겨두는 셈이라, 간을 바꾸려면 종이 자체를 다시 써야 해요. 설정 외부화는 이 값을 따로 붙이는 메모지(설정 파일)로 빼두는 일이에요.
🔤
주입(Inject) — 필요한 값이나 객체를 코드가 직접 만들지 않고, 스프링이 밖에서 넣어주는 것이에요(CH02의 DI와 같은 결). 설정 파일의 값을 스프링이 대신 찾아 필드에 "꽂아준다"고 보면 돼요.
📄 application.properties / application.yml 스프링 부트가 시작할 때 자동으로 읽는 설정 파일이에요. 키=값 형태로 값을 적어두면 앱 곳곳에서 꺼내 쓸 수 있어요. (둘은 형식만 다르고 역할은 같아요.)
# application.properties
server.port=8081
app.welcome-message=안녕하세요, 스프링!
💉 @Value — 값 하나를 콕 집어 주입 설정 파일의 값을 @Value("${키}") 문법으로 필드에 주입받아요. 코드에 값을 박지 않으니, 설정 파일만 바꾸면 코드 수정 없이 동작이 달라져요.
@Component
public class WelcomeService {

    // application.properties의 값을 주입받음
    @Value("${app.welcome-message}")
    private String message;

    @Value("${server.port}")
    private int port;
}
📦 @ConfigurationProperties — 여러 값을 묶어서 관련 설정이 많아지면 @Value를 하나씩 붙이기 번거로워요. @ConfigurationProperties같은 접두어(app.*)의 값들을 객체 하나로 통째로 받아줘서 훨씬 깔끔해요.
@Component
@ConfigurationProperties(prefix = "app")
public class AppProps {
    private String welcomeMessage;  // app.welcome-message
    // getter / setter ...
}
🔐
왜 외부화가 실무 필수일까. API 키·비밀번호를 코드에 박으면 소스와 함께 유출되기 쉽고, 환경(개발/운영)마다 값을 바꾸기도 어려워요. 설정을 파일로 빼두면 보안·유연성이 둘 다 좋아져요.
🌗
프로파일
개발용과 운영용, 설정을 갈라 쓰기
같은 코드, 다른 환경 — @Profile로 스위치.

개발할 때 쓰는 DB와 실제 서비스(운영)에 쓰는 DB는 주소도, 계정도, 키도 다 달라요. 그렇다고 매번 값을 바꿔가며 빌드할 순 없죠. 스프링은 환경별 설정 파일을 준비해두고 어떤 걸 쓸지만 골라주면 되게 해줘요.

🔤
프로파일(Profile) · 환경 — 환경은 코드가 실제로 도는 무대예요. 내 노트북에서 실험하는 개발(dev) 무대와, 진짜 손님이 쓰는 운영(prod) 무대는 DB 주소도 계정도 다르죠. 프로파일은 이 무대별 설정 묶음에 붙인 이름표예요. 카메라의 "실내 모드 / 야외 모드"처럼, 상황에 맞는 설정 세트를 통째로 갈아끼우는 스위치라고 생각하면 돼요. 코드는 그대로 두고 이름표만 바꾸는 거예요.
🛠️
dev · 개발
내 PC용 가벼운 DB(H2), 콘솔로 확인. 마음껏 실험하는 환경이에요.
같은 코드
다른 설정
🚀
prod · 운영
실제 서비스 DB(MySQL), 진짜 사용자. 설정만 prod로 바꾸면 끝이에요.
📁 환경별 설정 파일 — application-{환경}.properties 파일 이름에 환경을 붙여요. 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 실행할 때 어떤 프로파일을 켤지만 지정하면 돼요. spring.profiles.active=dev로 두면 개발용 설정이, prod로 바꾸면 운영용 설정이 적용돼요. 코드는 그대로.
🧩 @Profile — 빈도 환경별로 설정 값뿐 아니라 특정 빈(Bean)도 환경에 따라 다르게 등록할 수 있어요. @Profile("dev")가 붙은 빈은 dev 프로파일일 때만 만들어져요.
@Bean
@Profile("dev")   // 개발 환경에서만 이 빈 사용
public MailSender fakeMailSender() {
    return new ConsoleMailSender();  // 실제로 안 보내고 콘솔 출력
}
💡
한 줄 요약. "코드는 하나, 설정은 환경마다." 프로파일 덕분에 개발자는 dev로, 서버는 prod같은 코드를 다른 값으로 돌릴 수 있어요.
🔒
트랜잭션·AOP
@Transactional — 전부 성공 아니면 전부 취소
그리고 이걸 가능하게 하는 마법, AOP.

계좌 이체를 생각해봐요. "A에서 출금 → B에 입금", 이 두 작업은 반드시 함께 성공하거나 함께 실패해야 해요. 출금만 되고 입금이 실패하면 돈이 사라지니까요. 이렇게 여러 작업을 하나로 묶어 "모 아니면 도"로 처리하는 게 트랜잭션이에요. (CH08에서 본 SQL 트랜잭션과 같은 개념이에요.)

🔤
트랜잭션 · 커밋 · 롤백 — 트랜잭션(Transaction)은 "함께 성공하거나 함께 실패해야 하는 작업 묶음"이에요. 묶음이 끝까지 잘 끝나 그 결과를 확정 저장하는 게 커밋(commit), 중간에 문제가 생겨 처음 상태로 전부 되돌리는롤백(rollback)이에요. 계좌이체가 실패하면 출금까지 없던 일로 돌리는 것 — 그게 롤백이에요.
🔤
ACID — 트랜잭션이 지켜야 할 네 가지 성질의 머리글자예요. 원자성(Atomicity, 모 아니면 도), 일관성(Consistency, 규칙이 깨지지 않음), 격리성(Isolation, 동시에 일어나도 서로 안 꼬임), 지속성(Durability, 커밋하면 사라지지 않음)이에요. 지금은 특히 첫 글자 원자성 = "모 아니면 도"만 확실히 잡아도 충분해요.
✅ @Transactional — 메서드 하나 = 트랜잭션 하나 메서드에 @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 비밀은 AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)예요. 트랜잭션 시작/커밋/롤백 같은 코드는 수많은 메서드에 똑같이 반복돼요. AOP는 이런 공통 관심사(cross-cutting concern)를 핵심 로직과 분리해두고, 필요한 지점에 자동으로 끼워 넣어줘요. 💡 비유: 회의실에 들어갈 때마다 "불 켜고 → 회의하고 → 불 끄기". 여기서 불 켜기/끄기(공통 관심사)를 매번 손으로 안 하고 센서가 자동으로 해주는 것 — 그게 AOP예요. 회의(핵심 로직)에만 집중하면 돼요.
🔤
AOP · 횡단 관심사(cross-cutting concern) — 횡단 관심사는 트랜잭션·로깅·보안처럼 여러 기능을 가로질러 똑같이 필요한 일이에요. 건물로 치면 층마다 따로 있는 사무실(핵심 로직)이 아니라, 모든 층을 관통하는 전기 배선·소방 설비 같은 거죠. AOP는 이런 공통 배선을 한 번만 깔아두고 필요한 층마다 자동으로 이어주는 방식이에요.
🔤
프록시(Proxy, 대리 객체) — AOP가 실제로 동작하는 비결이에요. 스프링은 원본 객체를 그대로 쓰지 않고, 그 앞에 대신 서는 대리인(프록시)을 하나 만들어요. 손님(호출)은 먼저 이 대리인을 만나고, 대리인이 "트랜잭션 시작 → 진짜 메서드 실행 → 커밋/롤백"처럼 앞뒤 공통 작업을 챙긴 뒤 원본에게 넘겨요. 가게 앞에서 손님을 먼저 맞이하고 안내하는 도어맨을 떠올리면 돼요. 그래서 원본 코드는 한 줄도 안 고쳐도 돼요.
구분핵심 로직공통 관심사(AOP 대상)
이체 서비스출금·입금 계산트랜잭션(커밋/롤백)
주문 서비스주문 저장로깅, 실행 시간 측정
관리자 API데이터 조회권한 체크(보안)
🔤
로깅(logging) — 프로그램이 언제 무슨 일을 했는지 기록으로 남기는 것이에요. System.out.println 대신 SLF4J·Logback 같은 로깅 도구를 쓰면, 상황별로 기록 양을 조절하고 파일로도 남길 수 있어서 실무에선 이걸 써요.
🧠
정리. @Transactional결과(트랜잭션 묶기), AOP는 그걸 가능하게 하는 원리(공통 관심사 자동 적용)예요. 로깅·보안·트랜잭션이 대표적인 AOP 활용처예요.
🧪
테스트
코드가 진짜 잘 도는지 자동으로 검증
@SpringBootTest와 JUnit, 그리고 DI가 빛나는 순간.

기능을 만들 때마다 브라우저로 일일이 눌러 확인하는 건 느리고 실수하기 쉬워요. 테스트 코드를 짜두면 버튼 한 번으로 수십 개 기능을 자동 검증할 수 있어요. 스프링은 테스트를 아주 잘 지원해요.

🧷 @SpringBootTest — 통합 테스트 실제 앱을 실행할 때처럼 스프링 컨테이너를 통째로 띄워 두고 테스트해요. 컨트롤러→서비스→리포지토리가 연결된 상태 그대로 검증하니 "진짜 되는지"를 확인하기 좋아요. 검증에는 JUnit(자바에서 가장 널리 쓰는 테스트 도구)을 써요.
@SpringBootTest
class BankServiceTest {

    @Autowired
    BankService bankService;   // DI로 실제 빈 주입받음

    @Test
    void 이체_성공() {
        bankService.transfer(1L, 2L, 1000);
        // JUnit으로 결과 검증
        assertThat(accountRepo.balance(1L)).isEqualTo(0);
    }
}
🧩 단위 테스트 & 목(Mock) — DI가 빛나는 곳 계층 하나만 떼어 작게 테스트하고 싶을 땐, 진짜 DB 대신 가짜 객체(Mock)를 주입해요. CH02의 DI 덕분에, 진짜든 가짜든 바꿔 끼우기가 자유로워서 테스트가 쉬워져요. 즉, 잘 설계된(DI 기반) 스프링 코드는 테스트하기 좋은 코드이기도 해요. 이게 스프링을 쓰는 또 하나의 실무적 이점이에요.
🏗️
통합 테스트
앱을 통째로 띄워, 계층이 연결된 그대로 검증. "전체가 진짜 되나?"
🔩
단위 테스트
한 조각만 떼어 Mock을 끼워 빠르게 검증. "이 부품 하나가 맞나?"
핵심만. @SpringBootTest = 앱 전체를 띄우는 통합 테스트, Mock = 일부만 떼서 하는 단위 테스트. 둘 다 JUnit으로 결과를 검증해요.
🗺️
마무리
지금까지 배운 걸 한 장의 그림으로
퍼즐 조각들이 어떻게 하나로 맞물리는지 봐요.

여기까지 오느라 정말 고생 많았어요. 각 장에서 배운 개념들은 따로 노는 게 아니라 하나의 흐름으로 연결돼요. 큰 그림을 잡아볼게요.

🍃
IoC / DI
스프링이 객체를 만들고 연결
🫘
Bean · 컨테이너
관리되는 객체들의 통
🚀
Spring Boot
설정 자동화 · 바로 실행
🏛️
계층 구조
Controller·Service·Repository
🌐
REST API
JSON으로 요청·응답
🗄️
JPA
객체로 DB 다루기
🛡️
예외·검증
안전하고 친절한 응답
🏁
실무 개념
설정·프로파일·트랜잭션·테스트
🎉
완성
실무로 출발!
🎁 앞으로 더 파볼 만한 것
  • Spring Security — 로그인·권한(인증·인가) 처리
  • REST API 심화 — 페이징, DTO 변환, 문서화(Swagger)
  • 배포 — 빌드(Gradle/Maven), Docker, 클라우드
  • 모니터링 — 로그, 액추에이터(Actuator)로 상태 확인
🔤
빌드 & 배포 — 빌드는 소스 코드를 실행 가능한 하나의 파일(jar 등)로 묶는 과정(Gradle·Maven이 대신 해줘요), 배포는 그 결과물을 서버에 올려 실제로 돌리는 것이에요.
🔤
DTO & 엔티티 — 엔티티는 DB 테이블과 짝지어진 객체(CH08), DTO는 화면·API에 주고받을 값만 담은 객체예요. 둘을 나누면 내부 구조를 그대로 드러내지 않아 더 안전하고 유연해요.
👏
완주 축하해요, 태호님! 이제 스프링의 핵심 원리와 실무 개념을 모두 관통했어요. 문법을 다 외우지 못해도 괜찮아요 — "왜 이렇게 하는지"를 알면 나머지는 필요할 때 찾아 쓰면 돼요. 진짜 프로젝트에 도전해볼 시간이에요!
🧠 이 장 핵심 요약
📝
문제풀이 · 점검
마지막으로 가볍게 점검해봐요
시험이 아니라 "내가 이해했나" 확인용이에요. 틀려도 바로 해설이 나와요.
🧪
문제를 풀면 즉시 정답과 해설이 나오고, 위쪽 바에 점수가 쌓여요. 네 가지 유형(객관식 · O/X · 빈칸 · 개념)을 섞어 두었어요.
🎉 완주 🗺️ 전체 로드맵으로 — 지금까지의 여정을 한눈에 돌아보기