으아닛 벌써 두 번째 미션이 끝났다니.. 레벨 2의 절반이 지났다니..!
공부를 하면 할수록 레벨 2가 호락호락하지 않다는 생각이 든다. 하지만 괜찮다. 나도 호락호락하지 않으니깐 😎

🏃♂️ 미션 2: 방탈출 사용자 예약
레벨 2의 두 번째 미션은 "방탈출 사용자 예약"이다! 레벨 1과 달리, 레벨 2는 이전 미션에 이어서 다음 미션을 진행한다. 따라서 이전 미션에서 대충 덮어놓고 넘기면 다음에 큰코다친다...ㄷㄷ 내 이럴 줄 알았으면 테스트코드 미리미리 짜놨지 엉엉
미션 1이었던 방탈출 예약 관리가 스프링을 쓰기 위한 기초 지식 쌓기였다면, 미션 2는 웹 어플리케이션을 만들기 위한 기본기 쌓기 과정이었다. 유노왓암셍? 어떤 내용을 공부했는지는 차근차근 살펴보자.
미션 소개
- 새로운 요구사항을 반영하며 방탈출 예약 애플리케이션을 발전시켜 나갑니다.
- 사용자가 직접 방탈출 예약을 할 수 있도록 기능을 제공합니다.
- 역할(사용자, 관리자)에 따라 접근할 수 있는 페이지를 구분합니다.
- 랭킹, 조건 검색 등을 위한 쿼리를 작성하고, API로 제공합니다.
PR
1단계 - 예외 처리와 응답
이전까지는 예외 처리 없이 단순한 CRD API만 제공했었다. 그래서 서비스 계층이 하는 일이 거의 없었는데, 1단계에서 예외 처리 요구사항이 추가되면서 서비스 계층의 역할이 분명해지기 시작했다. 스프링에서 예외를 처리하고 응답하는 다양한 방법을 배울 수 있었고, 계층별 책임이 조금씩 명확해지는 경험을 했다.
2단계 - 테마 추가
2단계에서는 "테마" 도메인이 추가됐다. 도메인이 추가되면서 기존 코드가 전부 깨지는 악몽 같은 경험을 했다 😇
특히 DB 테이블 구조가 수정되면서 어려움을 많이 겪었다. 실제 프로젝트에서는 DB를 최대한 수정하지 않는다고 알고 있는데, 영향 범위가 너무 크기 때문이다. 미션에서도 작은 DB 변화가 코드 전체에 큰 영향을 미치는 것을 경험하며 사전 도메인/DB 설계의 중요성을 절실히 느꼈다. 근데 또 한편으로는 절대 변하지 않는 DB를 만드는 게 가능한가? 하는 생각이 들기도 하고 흠 아직 이 부분은 잘 모르겠다.
처음에는 무작정 코드를 바꾸고 빨간줄이 뜨는 부분을 찾아 수정하는 방식으로 접근했는데, 이 방식은 정말 비효율적이었다. 이후에는 더 체계적인 방법을 시도했다:
- 기존 원본을 놔두고, 수정할 복사본을 만든다
- 원본을 사용하는 코드를 찾아 복사본을 사용하도록 변경한다
- 원본을 사용하는 곳이 없을 때 원본을 삭제한다
처음에는 익숙하지 않았지만 연습하다 보니 이 방법이 훨씬 안전하고 효율적이라는 것을 깨달았다. 다음 페어에게도 이 방법을 꼭 전파해야지!
3단계 - 사용자 기능 (예약 가능한 시간, 인기 테마 조회)
3단계에서는 "예약 가능한 시간 조회", "인기 테마 조회" API가 요구사항으로 추가됐다. 이전의 단순한 CRD 작업을 넘어서는 API를 구현하면서 Service와 Repository의 역할 구분, DTO의 필요성을 더 깊이 이해할 수 있었다. 데이터를 여러 방식으로 가공하고 조회하는 과정에서 각 계층의 책임이 더욱 명확해졌다.
STEP2: 4단계 ~6단계
4단계 - 사용자 로그인
4단계에서는 회원가입, 로그인, 로그아웃 기능을 구현했다. 학습 자료를 참고하여 JWT 토큰과 쿠키를 이용한 인증 시스템을 구현했다.
이 단계에서는 이전에 미뤄두었던 테스트 코드를 채우면서 진행했다. 시간이 많이 걸려 미션 진행 속도가 느려진 점은 아쉬웠지만, 테스트 코드 작성을 통해 얻은 것도 많았다. 특히 TDD 방식으로 개발하면서 내 구현에 확신과 안정감을 얻을 수 있었다. 미션2를 할 때는 바텀업 방식을 사용했고, 미션 3을 하는 지금은 탑다운을 사용하고 있는데 좀 더 정리를 하고 미션 3 회고글에 TDD를 써보겠다! 그런데 약간 아쉬운 점은 테스트에 밀려서 로그인 구현에 대한 고민을 깊게 하지 못했다. 일단 돌아가는 대로 했다. 그래도 좋은 리뷰어를 만나서 리뷰를 받는 과정에서 추가로 많이 공부할 수 있었다.
5단계 - 로그인 리팩터링
5단계에서는 HandlerMethodArgumentResolver를 이용해 인증을 처리하고, 인증된 멤버 정보를 컨트롤러의 메서드 파라미터로 넘겨주는 방식으로 리팩터링했다. 이 과정에서 DispatcherServlet의 동작 원리도 살짝 들여다볼 수 있었다. 아주 유용해 이 녀석~
6단계 - 관리자 기능
6단계에서는 관리자 페이지 접근 권한을 관리하기 위해 Interceptor를 도입했다. 이를 통해 사용자 역할에 따른 접근 제어를 구현하며 인증(Authentication)과 인가(Authorization)의 차이를 실제로 경험할 수 있었다.
✏️ 무엇을 배웠나?
위에서 주저리주저리 쓰긴 했지만, 또 무엇을 했나 다시 한번 정리해 보자!
학습 목표
- HTTP 프로토콜에 대한 이해와 구현할 수 있다. ✅
- HTTP 프로토콜의 HTTP 메서드, 요청과 응답 헤더, 상태 코드 등의 요소가 무엇인지 안다. ✅
- 다양한 HTTP 메서드(GET, POST, PUT, DELETE)의 용도와 차이점을 이해하고 설명할 수 있다. ✅
- 상태 코드(200, 404, 500 등)의 의미와 적절한 사용 상황을 이해하고 적용할 수 있다. ✅
- 요청에 대한 API를 설계하고 구현할 수 있다. ✅
- 리소스 중심의 API 설계 방법을 이해하고, URI를 통해 리소스를 표현하는 방법을 설명할 수 있다. ✅
- Spring MVC가 제공하는 어노테이션을 사용해서 API 설계에 맞는 요청을 받고 응답할 수 있다. ✅
- 사용자 인증 방식이 무엇인지 알고, 이를 코드에 적용할 수 있다. ✅
- 사용자 인증과 권한 부여(인가)가 무엇이며 어떤 상황에 필요한지 설명할 수 있다. ✅
- HTTP Basic, Token, Session 기반의 인증이 무엇인지 알고, 구현할 수 있다. ✅
- Spring이 제공하는 HandlerInterceptor, HandlerMethodArgumentResolver의 구현체를 만들어 보고 이 인터페이스를 어떤 상황에 사용할 수 있는지 이해한다. ✅
- 필요한 데이터 조회를 위한 쿼리 작성 ✅
- 다양한 데이터 필터링 조건(WHERE, AND, OR, IN, NOT IN)을 사용하여 데이터를 정확하게 조회할 수 있다. ✅
- JOIN (INNER, LEFT, RIGHT, FULL)을 사용하여 여러 테이블 간의 관계를 이해하고, 관련된 데이터를 결합하여 조회할 수 있다. ✅
- GROUP BY와 함께 집계 함수(COUNT, SUM, AVG, MAX, MIN)를 사용하여 그룹화된 데이터의 요약 정보를 추출할 수 있다. ✅
- ORDER BY를 사용하여 쿼리 결과를 특정 기준에 따라 정렬할 수 있다. ✅
- 테스트 도구를 사용해서 웹 요청/응답에 대해 자동으로 검증할 수 있다. ✅
- 개발한 기능을 테스트를 사용해 자동으로 검증할 수 있다. ✅
- 다양한 HTTP 메서드를 사용하는 요청을 보나고, 응답을 검증할 수 있다. ✅
오호 이렇게 보니까 모든 학습 목표를 잘 달성했네? 뭔가 아쉽다고 느꼈는데, 그럴 필요 없었다! 잘했다 내 자신~~
학습 자료 & 수업을 통해 배운 것
- Exception 처리
- @ExceptionHandler
- @ControllerAdvice
- 좋은 웹 API란 무엇인가
- Validation의 위치
- 인증
- Basic Auth
- Session
- Token
- MVC와 DipatcherServlet
- Interceptor
- ArgumentResolver
- 브라운의 테스트 특강
- Fake vs Stub vs Mock
📚 추가 학습 내용
미션 요구사항 외에도 리뷰 과정에서 생긴 궁금증을 해결하기 위해 다음과 같은 주제들을 추가로 공부했다.
Layered Architecture & 패키지 구조
기존의 패키지 구조에서는 service 패키지 내부에 도메인 패키지가 존재했다. 리뷰어가 "서비스 계층 안에 도메인이 속하는 것이 맞는지"에 대해 질문했고, 이를 계기로 계층형 아키텍처에 대해 깊이 고민하게 됐다.
O'Reilly 블로그의 Layered Architecture 아티클을 읽으며 계층형 아키텍처의 핵심 개념을 공부했다. 이 문제에 대해서는 정말 하루 종일 공부하고 다양한 크루들의 의견을 물어보았다. 흥미롭게도 각자 계층형 아키텍처에 대해 다른 생각을 가지고 있었으며, DDD를 공부한 크루들은 또 다른 구조를 적용하고 있었다. 여기서 DDD에 대한 궁금증이 생겨 다른 크루에게서 관련 책을 빌려서 발췌독을 하기도 했다(thanks to 메이)
이 과정에서 배운 내용을 정리해 보자면 다음과 같다. 일부는 리뷰어 찰리의 코멘트를 인용했다ㅎㅎ (thanks to 찰리)
- 계층형 아키텍처의 핵심은 관심사의 분리와 계층의 격리다.
- 관심사의 분리 : 하나의 계층을 구성하는 컴포넌트끼리는 하나의 관심사만 처리한다.
- 격리 계층: 계층 사이에서는 바로 아래 계층으로만 요청을 전달할 수 있다.
- 관심사의 분리와 계층의 격리를 통해 개발/유지보수/테스트 용이성을 확보 수 있다.
- 도메인은 그 자체로 독립적인 계층이며, 외부 계층에 의존하지 않는다
- 도메인은 비즈니스 로직의 핵심 규칙을 담은 계층으로, 가장 외부의 변화로부터 지켜야 하고 중요한 계층이다.
- 도메인이 중심을 잡고 있으면 "Controller, Service, Repository , 아키텍처, 프레임워크" 해당 부분을 100번 바꿔도 비즈니스 규칙은 변경되지 않는다.
- DTO는 특정 계층의 뷰로 이해하면 편하다.
토큰 & 쿠키
인증을 구현하면서 생긴 여러 궁금증에 대해 추가 학습을 진행했다. 특히 왜 요구사항에서 토큰과 쿠키를 함께 사용하도록 했는지 의문이 들었다. 학습 자료에서는 세션에 쿠키를 사용하고, 토큰은 Authorization 헤더를 통해 전달하는 방식을 보여주었기 때문이다.
찾아본 결과, JWT 토큰을 반드시 Authorization 헤더에 저장해야 한다는 규칙은 없었다. 이 방식은 OAuth 표준을 따를 때 주로 사용된다는 것을 알게 됐다. 단순히 AccessToken 하나만 사용하는 구현에서는 다양한 전달 방식을 선택할 수 있었다.
더 조사하면서 쿠키는 서버에서 완전히 관리할 수 있다는 점을 깨달았다. 처음에는 클라이언트가 쿠키를 통해 작업한다고 생각했는데, 사실은 서버가 브라우저에 쿠키를 심어주고 설정도 서버가 제어한다는 것을 배웠다. 또한 브라우저가 요청 시 자동으로 쿠키를 전송해주기 때문에, Authorization 헤더 대신 쿠키를 사용하면 클라이언트 측에서 별도의 토큰 관리 코드가 필요 없다는 장점도 있었다.
쿠키 학습 과정에서 다양한 쿠키 옵션(HttpOnly, Secure, SameSite, Path, MaxAge)의 역할도 이해했고, 자연스럽게 CSRF, XSS와 같은 보안 관련 지식도 습득할 수 있었다.
필터 vs 인터셉터
인터셉터를 통해 인증/인가를 구현한 후, 리뷰어로부터 인터셉터와 필터의 차이에 대한 질문을 받았다. 이를 계기로 두 컴포넌트의 차이점과 각각의 사용 시나리오를 학습했다.
'후기 or 회고 > 우아한테크코스' 카테고리의 다른 글
| [우테코 Lv2] 레벨2 미션4 방탈출 결제 / 배포 회고 (7) | 2025.06.17 |
|---|---|
| [우테코 Lv2] 레벨2 미션3 방탈출 예약 대기 회고 (8) | 2025.06.02 |
| [우테코 Lv2] 레벨2 미션1 방탈출 예약 관리 회고 (4) | 2025.05.04 |
| [우테코 Lv1] 레벨1 중간(?) 회고 (2) | 2025.03.17 |
| 우아한테크코스 7기 백엔드 최종 합격 후기 2 (최종코테 준비) (0) | 2025.01.17 |