1주일의 짧은 방학이 끝나고 4월 15일에 레벨 2가 시작됐다!
레벨 2는 웹어플리케이션을 개발하기 위한 역량을 기르는 과정이다. 총 8주 과정이며, 2주씩 4개의 미션으로 구성되어 있다.
레벨 1에서 회고 글을 거의 남기지 못한 것에 대한 아쉬움이 있어서, 레벨 2부터는 미션 단위로 회고글을 작성하려고 한다 😙
🏃♂️ 미션 1: 방탈출 예약 관리
레벨 2 첫 번째 미션은...? 두구두구두구.. "방탈출 예약 관리"입니다!!
첫 번째 미션이라서 그런지, 아주 간단한 기능만 요구하는 미션이었다. 이 기간에 다들 입을 모아서 "생각보다 레벨 2 여유로운데?"라고 말했다ㅎㅎ 미션을 해결하는데 시간이 오래 걸리지 않아서 미션을 빨리 해결하고 우테코에서 제공하는 학습자료들을 보면서 공부를 했다. 또한 이렇게 공부를 하면서 미션 2 공부 방식에 대해 나름의 기준을 세웠다.
미션 소개
- 방탈출 예약을 관리할 수 있는 웹 애플리케이션을 만든다.
- 예약 페이지에 필요한 기능을 단계적으로 추가하고 확장하는 경험을 한다.
- 구현한 기능을 테스트를 통해 검증한다.
PR
페어와 함께 진행한 STEP1!
요구사항에 맞춰 정적 페이지를 서빙하고, 간단한 예약 CRD API를 제공하는 프로그램을 만들었다.
정말 간단해서 한 3시간 만에 구현을 끝내고 페어와 헤어졌다 😅
STEP1의 코드에 h2 데이터베이스를 적용했다.
또한 추가적인 요구사항에 맞춰서 "예약" 도메인에 "시간" 도메인을 추가하고 관련 API들을 추가/수정하고, Layerd Architecture를 적용해 계층화 리팩터링을 했다.
✏️ 무엇을 배웠나?
첫 번째 미션을 하면서 배운 것들을 정리해 보자!
학습 목표
레벨 2를 시작하면서 각 미션에서 학습해야 할 것들을 아주 친절하게 수업시트로 정리해서 제공해 주었다.
학습 목표를 돌아보면서 내가 배운 것을 체크해 보자. 스스로 달성했다고 느껴지는 항목에는 ✅ 표시를, 좀 더 학습하고 싶은 항목에는 ⚠️ 표시를 했다.
- 로컬 환경(localhost)에서 웹 애플리케이션을 실행 및 테스트할 수 있다. ✅
- 웹 브라우저를 사용하여 애플리케이션의 페이지에 접속하고, 기능을 테스트할 수 있다. ✅
- 스프링부트 프레임워크를 사용해 웹 요청을 받고 응답할 수 있다. ✅
- URL과 HTTP Method에 따라 이를 처리할 컨트롤러를 매핑할 수 있다. ✅
- 어노테이션을 사용해 HTTP 요청에 포함된 request parameter, request body를 컨트롤러 메서드에 바인딩할 수 있다. ✅
- 템플릿 엔진을 사용하여 동적 웹 페이지를 응답할 수 있다. ✅
- 웹 애플리케이션에서 DB에 데이터를 저장하고 조회할 수 있다. ⚠️
- application.properties 또는 application.yml 파일에서 데이터 베이스 연동에 필요한 정보를 설정할 수 있다. ⚠️
- JdbcTemplate을 사용하여 SELECT, INSERT, UPDATE, DELETE 쿼리를 작성하고 실행할 수 있다. ✅
- Inmemory DB가 무엇인지 이해하고, 애플리케이션 개발에 적용할 수 있다.✅
- 스프링 빈의 의존성 주입이 무엇인지 알고, 코드에 적용할 수 있다. ✅
- 어노테이션을 사용해 스프링이 관리할 객체를 설정할 수 있다. ✅
- 소프트웨어 개발에 일반적으로 적용되는 계층형 아키텍처(Layered Architecture)를 알고, 코드에 적용할 수 있다. ⚠️
- 예약 관리 어플리케이션에 계층형 아키텍처를 적용해 본다. ✅
- 학습 테스트를 활용해 새로운 기능을 학습할 수 있다. ✅
- 새로운 기능을 익혀야 할 때, 학습 테스트를 작성하고 실행하는 방식으로 학습하고 코드에 적용할 수 있다. ✅
application.properties 또는 application.yml 파일에서 데이터 베이스 연동에 필요한 정보를 설정할 수 있다. ⚠️
→ 예시 코드에 나온 것을 복붙 해서 설정할 수는 있지만, 제대로 알고 쓰지는 못한 것 같다.
소프트웨어 개발에 일반적으로 적용되는 계층형 아키텍처(Layered Architecture)를 알고, 코드에 적용할 수 있다. ⚠️
→ 코드에 적용해 봤고, 공부도 했다! 하지만 공부할수록 계층형 아키텍처는 정말 다양하고 더 공부가 필요한 부분이라고 생각한다ㅎㅎ
Gradle
학습 자료 중에 "Gradle 프로젝트 구축하기"가 있었다. 평소에 그레이들을 쓸 때 그냥 따라 쓰기만 했지, 제대로 알고 쓰지 못해서 이에 대한 궁금증을 가지고 있었다. 그래서 미션 STEP1을 빨리 끝내고 남는 시간에 평소에 궁금했던 것 위주로 그레이들에 대해서 좀 더 공부해 봤다.
공부한 내용은 곧 티스토리에 추가 작성할 예정이다!
CSS / SSR
미션 프롤로그 질문 중에 렌더링에 대한 질문이 있었다. 또한 CSS/SSR에 대한 질문도 있었다.
미션을 구현할 때는 렌더링을 생각해 볼 필요가 없었는데, 프롤로그를 작성하면서 이 부분에 대해 더 공부가 필요하다고 느꼈다. STEP1에서 구현했던 프로그램을 뜯어보면서 내가 만든 서비스가 어떤 과정을 통해 브라우저 화면에 띄워지는지 공부했다. 코드만 보고는 의문이 풀리지 않는 것들이 많아서 브라우저 개발자 도구의 네트워크 탭을 보면서 추측을 하고, GPT에 물어보면서 새로운 것을 많이 알게 됐다.
대표적으로 궁금했던 것은, "나는 HTML 파일을 서빙하는 컨트롤러만 작성했는데 어떻게 CSS, JS가 적용되고 있지?" "JSON을 호출하는 API는 언제 어떻게 호출되고 있는 거지?"였다. 개발자 도구를 보면서, 브라우저가 HTML 파일을 받은 후 HTML 파일을 파싱해 정적 파일을 추가 요청하는 것을 발견했다! 정말 웹 프로젝트를 많이 해봤는데 이걸 처음 알았다니.. 꽤 흥미로웠다. 그리고 제공된 자바스크립트 파일에서 랜더링이 끝나는 시점과 특정 버튼에 이벤트 리스너를 통해 API 호출 함수를 등록해서 사용하는 것을 알게 됐다.
그리고 CSS/SSR에 대해 공부했는데, 관련한 프롤로그 질문을 답변하다가 큰 의문이 생겼다. 프롤로그 질문에서 "이번 미션에서 ~~ 서버 사이드 렌더링으로 페이지를 응답하는 경험을 했습니다."라고 나와있었다. 그런데 음... 앞에서 프로젝트 렌더링 과정을 탐구해 본 경험으로, 미션에서 렌더링 하는 방식은 SSR보다는 CSR에 가깝다고 생각했다. GPT에도 물어봤는데 GPT는 SSR이 맞다고 해서 더 헷갈렸다. 다른 크루들도 다들 비슷한 고민을 하고 있어서 정말 다양한 사람들과 이 이야기를 했다. 한 10명한테는 물어본 듯ㅋㅋ 지나가다가 어떤 크루들이 관련 이야기를 하는 걸 보고 같이 이야기하다가 1시간 넘게 붙잡혀있기도 했다. 나는 CSR로 결론을 내렸다.
Layerd Architecture
방탈출 예약 관리 미션의 9단계에서 계층화 리팩터링을 진행했다. 예시에 나온대로 나는 Controller / Service / Repository 3개의 계층으로 클래스를 만들어 사용했다. 사실 근데 나누기는 나눴지만, 미션의 기능이 거의 없었기 때문에 서비스 계층이 사실상 하는 일이 잘 없었고, 내가 Layerd Architecture를 잘 사용한 것인지에 대한 확신이 없었다.
미션의 10단계는 "콘솔 UI 지원"이었다. 같은 기능을 콘솔에서도 사용할 수 있도록 콘솔 UI에 추가하고, 데이터는 DB가 아닌 메모리상에만 저장하도록 하는 것이 요구사항이었다. 이 미션은 선택 미션이었기 때문에 필수로 하지 않아도 괜찮았다. 10단계를 할 시간이 별로 없었지만 이 선택 미션이 꼭 해보고 싶었다. 그래서 10단계를 끝까지 하지는 못해도 시간이 되는대로 10단계를 구현해 봤고, Layered Architecture에서 서비스 계층의 필요성을 제대로 느낄 수 있었다!
10단계 선택 미션을 꼭 해보고 싶었던 이유가 있었다. Level1에서 MVC 아키텍처를 사용할 때 "입출력 로직과 데이터 로직을 분리해야 한다.", "뷰는 바뀌기 쉽다.", "지금은 콘솔 UI만 사용하지만, 나중에 다른 UI가 추가될 경우 MVC가 유용하다"와 같은 이야기를 많이 했다. 또한 Level2의 Layered Architecture에 대해서도 비슷한 이야기를 들은 것 같다. 그런데 사실 우리가 콘솔 UI와 다른 UI를 동시에 도입해 본 적이 없어서 이게 잘 실감이 나지 않았었다. 그래서 10단계를 하면, 이 궁금했던 부분을 제대로 느낄 수 있을 것 같았고, 실제로도 그랬다.
콘솔 UI에서 Layered Architecture가 어떻게 사용됐는지 간단히 설명해보겠다.
Controller?
웹 UI를 사용하는 기존의 프로그램은 컨트롤러를 통해 사용자와 통신했다. 그런데 콘솔 UI는 당연히 이 컨트롤러를 사용할 수 없었다.
그래서 컨트롤러를 대신해서 입출력을 처리하는 View 클래스를 만들었다. 대충 아래와 같은 모습이다.
public final class View {
private final Scanner scanner = new Scanner(System.in);
/* 생성할 시간을 받는 메서드 */
public ReservationTimeRequest readTime() {
System.out.print("""
[시간 추가]
추가할 시간을 입력해주세요. (ex. 10:00)
>\s""");
LocalTime time = LocalTime.parse(scanner.nextLine());
return new ReservationTimeRequest(time);
}
/* 생성한 시간을 출력하는 메서드 */
public void displayNewTime(final ReservationTimeResponse reservationTimeResponse) {
System.out.printf("""
시간이 추가됐습니다.
순서: %d
시간: %s
""", reservationTimeResponse.id(), reservationTimeResponse.startAt());
}
}
코드를 보면 알 수 있듯이 콘솔 프로그램에서 View는 웹 프로그램의 Controller에 대응한다고 보기는 어렵다. 보통 "프론트엔드"라고 불리는 HTML, CSS, Javascript 코드에 가깝다고 볼 수 있다. 컨트롤러는 API 요청과 응답의 흐름을 담당하고, 서비스를 호출해서 요청의 처리를 위임하는 역할을 한다. 콘솔 UI에서 이 역할을 담당하는 Console 클래스를 만들었다. 대충 아래와 같은 모습이다!
public final class TimeConsole {
private final View view;
private final ReservationTimeService timeService = new ReservationTimeService(new MemoryReservationTimeDao());
public TimeConsole(final View view) {
this.view = view;
}
public void addTime() {
final ReservationTimeRequest reservationTimeRequest = view.readTime();
final ReservationTimeResponse reservationTimeResponse = timeService.createReservationTime(
reservationTimeRequest);
view.displayNewTime(reservationTimeResponse);
}
}
이 과정을 통해 다음의 내용을 경험할 수 있었다.
- DTO의 유용함 : View는 DTO만 알면 된다. 입력을 받는 과정이 어떻게 되든, Request DTO의 형식에 맞게 포장해서 전달해 주면 된다. 결과값을 출력할 때는 View는 그저 Response DTO를 통해 넘어오는 값들에 대해서만 신경 쓰면 된다. 서버의 내부에서 무슨 일이 일어나는지 전혀 신경 쓰지 않고 오로지 View의 로직(입력/출력)에만 집중할 수 있다.
- 프레젠테이션 계층의 의미: Layered Architecture를 검색했을 때 맨 위에 오는 계층을 Presentation Layer로 명명하는 글들이 많았다. 처음에 미션에서 Layered Architecture를 사용할 때 별생각 없이 나는 Controller / Service / Repository 이렇게 3 계층을 사용할 거야!라고 생각했다. 그런데 콘솔 UI를 도입하니까 컨트롤러를 사용할 수 없었고, 조금 더 적합한 이름을 붙여서 Console+View 클래스를 사용했다. 컨트롤러 계층이 결국 프레젠테이션 계층에 속하는 것이구나 느꼈다. 뭐 이름은 아직까지 크게 중요한 것 같지 않다. 좀 더 공부해봐야 할 듯.
Repository
콘솔 프로그램에서는 DB를 사용하지 말고 메모리에 저장하라는 요구사항이 있었다. 기존 프로그램에서는 `ReservationTimeDao` 클래스에서 DB 로직을 처리했는데, 이를 사용할 수 없었기 때문에 `ReservationTimeDao`를 인터페이스로 바꾸고 `H2ReservationTimeDao`와 `MemoryReservationTimeDao`를 각각 구현했다. 즉, 저장에 대한 내용을 추상화한 후 프로그램에 따라 갈아 끼울 수 있도록 했다.
public final class MemoryReservationTimeDao implements ReservationTimeDao {
private final List<ReservationTime> reservationTimes = new ArrayList<>();
private final AtomicLong index = new AtomicLong(0);
@Override
public ReservationTime createReservationTime(final ReservationTime requestedTime) {
ReservationTime savedTime = new ReservationTime(index.incrementAndGet(), requestedTime.getStartAt());
reservationTimes.add(savedTime);
return reservationTimes.get(index.intValue() - 1);
}
}
아래 코드처럼 서비스를 생성할 때 생성자를 통해 DAO 구현체를 주입받도록 했다.
public final class TimeConsole {
private final View view;
// 콘솔 프로그램에서는 Memory DAO 구현체를 주입해서 사용한다.
private final ReservationTimeService timeService = new ReservationTimeService(new MemoryReservationTimeDao());
public TimeConsole(final View view) {
this.view = view;
}
public void addTime() {
final ReservationTimeRequest reservationTimeRequest = view.readTime();
final ReservationTimeResponse reservationTimeResponse = timeService.createReservationTime(
reservationTimeRequest);
view.displayNewTime(reservationTimeResponse);
}
}
이 과정을 통해 레포지터리 추상화의 필요성을 느낄 수 있었다. 또한 결국 레포지터리의 역할은 도메인 객체의 생명주기를 관리하는 것이기 때문에, 저장할 도메인 객체를 직접 넘겨주는 것이 더 자연스러운 구조라는 생각을 가지게 되었다.
Service
콘솔 UI로 프로그램을 전환하면서 컨트롤러, 레포지터리는 위와 같이 바뀌었다.
하지만 서비스는 코드 한 줄 바꾸지 않고 그대로 재사용이 가능했다 ‼️
외부 입출력은 컨트롤러가 처리하고, 저장 방식은 레포지터리가 처리하면서 서비스 계층은 외부 의존성을 전혀 가지지 않기 때문에 가능한 일이었다.
오호라 이 부분을 통해 Layered Architecture의 진짜 의의를 비로소 느꼈다. MVC 아키텍처가 Model 계층을 견고하게 만들기 위해 존재한다면, Layered Architecture는 Service 계층을 순수하게 만들기 위해 존재한다고 느껴졌다. 즉, UI와 DB가 변경되어도 그 핵심인 비즈니스 로직은 바뀌지 않는다. 따라서 계층을 통해 외부 의존성을 분리하고 서비스에서 비즈니스 로직을 처리하면 UI와 DB가 변경되어도 서비스 계층을 그대로 가져다 쓸 수 있다. 그래서 계층 간의 독립성이 중요한 것이다.
방탈출 예약 관리를 마무리하며
이렇게 적고 보니까 첫 번째 미션에서 배워야 할 것들을 잘 배운 것 같다.
미션이 쉽다고 느껴지니까, 너무 노는 시간을 많이 가져간 점은 아쉽다. 그래도 어쩔 수 없지 이미 지났는걸ㅎㅎ 주말 약속은 최대한 쳐내고 여유 있을수록 그 여유시간을 내 공부에 투자하는게 맞는 것 같다ㅎㅎ
회고글 분량상 다 적지는 못했지만 "카인드 러버덕", "강의대장" 이 두 활동을 진행했는데 둘다 만족스러웠다. 러버덕으로서는 레벨2 내내 활동을 해야하니까 앞으로 좀 더 다양한 방식으로 접근해보고 싶다. 강의대장으로서 진행한 내용은 곧 블로그에 포스팅 할 예정이다.
두 번째 미션도 화이팅 🔥
'후기 or 회고 > 우아한테크코스' 카테고리의 다른 글
| [우테코 Lv2] 레벨2 미션3 방탈출 예약 대기 회고 (8) | 2025.06.02 |
|---|---|
| [우테코 Lv2] 레벨2 미션2 방탈출 사용자 예약 회고 (6) | 2025.05.19 |
| [우테코 Lv1] 레벨1 중간(?) 회고 (2) | 2025.03.17 |
| 우아한테크코스 7기 백엔드 최종 합격 후기 2 (최종코테 준비) (0) | 2025.01.17 |
| 우아한테크코스 7기 백엔드 최종 합격 후기 1 (서류/프리코스 준비) (2) | 2025.01.15 |
