<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yuni.log</title>
        <link>https://velog.io/</link>
        <description>꾸준히 성장하는 백엔드 개발자</description>
        <lastBuildDate>Tue, 23 Dec 2025 07:33:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yuni.log</title>
            <url>https://velog.velcdn.com/images/dd-jiyuni/profile/d8ad1b20-0f3e-46c0-8e8a-6bd0aa1b765b/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yuni.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dd-jiyuni" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[LXP] 달라도 괜찮아요!]]></title>
            <link>https://velog.io/@dd-jiyuni/LXP-%EB%8B%AC%EB%9D%BC%EB%8F%84-%EA%B4%9C%EC%B0%AE%EC%95%84%EC%9A%94</link>
            <guid>https://velog.io/@dd-jiyuni/LXP-%EB%8B%AC%EB%9D%BC%EB%8F%84-%EA%B4%9C%EC%B0%AE%EC%95%84%EC%9A%94</guid>
            <pubDate>Tue, 23 Dec 2025 07:33:54 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/potenup-kllhy/roadmap">2개월 LXP 프로젝트</a>를 마치고, 이어서 3개월 LXP 프로젝트를 시작하게 되었습니다.
<del>(시간이 너무 빠르네요,,)</del></p>
<h2 id="2개월-lxp-vs-3개월-lxp">2개월 LXP vs. 3개월 LXP</h2>
<p>2개월 LXP와 비교했을 때, 이번 3개월 LXP에서 가장 크게 달라진 점은 아래와 같습니다.</p>
<ul>
<li>프론트엔드와의 협업</li>
<li>기존 로드맵 형식과는 다른 LXP 기획</li>
<li>프로젝트 운영 방식 및 코드 구조</li>
</ul>
<p>제가 포텐업에서 가장 매력적으로 느꼈던 것 중 하나가 바로 <strong>&quot;프론트엔드와의 협업&quot;</strong> 이었고, 이번 프로젝트에서 그 경험을 실제로 해볼 수 있었습니다.</p>
<p>이번 글에서는 협업 과정에서 느낀 점과, 기능 개발을 진행하며 마주했던 고민들, 그리고 새롭게 정리된 생각들을 중심으로 회고해보려 합니다.</p>
<h2 id="들어가기-전">들어가기 전</h2>
<p><a href="https://velog.io/@dd-jiyuni/LXP-roadmap.sh-%EC%8D%A8%EB%B3%B4%EC%85%A8%EB%82%98%EC%9A%94#%EB%8B%A4%EC%9D%8C-%EB%AA%A9%ED%91%9C">이전 회고에서의 목표</a>가 무엇이었는지 확인해보겠습니다.
<del>생각보다 이전 프로젝트에서 목표를 많이 잡았더라구요,,🥹</del></p>
<ol>
<li>어그리게이트 경계 잡는 연습 더 해보기</li>
<li>조회 모델 설계 연습 (DTO / Projection / Read Model)</li>
<li>단순 DTO를 넘어서, “이 화면을 위한 Read Model은 무엇인가?”를 먼저 고민하고 구성해 보기</li>
<li>이해가 막히면 설계 단계에서 질문하기</li>
<li>작은 단위로 자주 PR 보내기</li>
<li>도메인 시나리오를 설명하는 테스트를 꾸준히 쌓아가기</li>
</ol>
<p>이제 위 목표들을 중심으로,
이번 프로젝트에서 어떤 시도를 했고 어떤 고민을 거쳤는지 차근차근 정리해보겠습니다.</p>
<h2 id="gorogoro-프로젝트">&#39;gorogoro&#39; 프로젝트</h2>
<blockquote>
<p><a href="https://github.com/sansung-gorogoro">Github 보러가기</a></p>
</blockquote>
<p>1, 2개월 차 프로젝트와 달리 이번 프로젝트에서는 프론트엔드와 협업을 진행하며
매번 새로운 프로젝트를 만드는 방식이 아니라, 하나의 서비스를 기준으로 기능을 점진적으로 확장해 나가는 방식으로 개발을 진행했습니다.</p>
<p>개발을 시작하기 전 프론트엔드 분들과 논의하여 1차 MVP로 가져갈 기능들을 먼저 정의했고,
저는 그중 Cart(장바구니) 도메인을 맡아 구현하게 되었습니다.</p>
<h3 id="왜-헥사고날-아키텍처였나">왜 헥사고날 아키텍처였나?</h3>
<p>저희 프로젝트는 이번에 헥사고날 아키텍처를 사용하게 되었습니다.</p>
<p>헥사고날 아키텍처를 선택한 데에는 “이번엔 구조를 끝까지 가져가 보고 싶다”는 목표와 함께,
처음부터 도메인을 보호하는 구조를 전제로 개발을 시작해보고 싶다는 의도가 있었습니다.</p>
<p>이전 프로젝트들에서는 구조를 의식하긴 했지만,
개발 속도나 편의성을 이유로 도메인과 인프라가 점점 섞이는 경험을 했습니다.</p>
<p>실제로 적용해보니, 기능 하나를 추가할 때마다
“이 로직을 어디에 두는 게 맞을까?”에서 계속 멈추게 되었고,
그제서야 구조를 이해했다고 착각하고 있었다는 걸 깨달았습니다.</p>
<p>그 과정에서 도메인 로직을 작성할 때 DB나 프레임워크를 거의 의식하지 않게 되었고,
<strong>“이 코드는 어디에 있어야 하는가?”</strong>를 자연스럽게 기준으로 삼게 되었습니다.</p>
<p>이 과정 자체가 설계 연습이 되었고,
이전보다 구조에 대해 훨씬 많은 고민을 하게 만든 선택이었다고 느낍니다.</p>
<h3 id="패키지-구조">패키지 구조</h3>
<p>모든 세부 구현을 나열하기보다는,
제가 가장 많이 고민했던 지점이 드러나는 구조 위주로 핵심 패키지와 클래스만 정리해보았습니다.</p>
<pre><code class="language-java">├── cart
│   ├── application // 유스케이스 조합 및 흐름 제어
│   │   ├── port
│   │   │   ├── in // 서비스가 제공하는 기능 명세 (UseCase) : 유일한 진입 인터페이스 
│   │   │   └── out // 외부 시스템이나 인프라에 대한 의존성 계약
│   │   └── service
│   │       ├── CartCommandService.java // 생성, 수정, 삭제 흐름 담당
│   │       └── CartQueryService.java  // 조회 흐름 담당
│   ├── common // 공통으로 사용하는 기술 요소 ex) 공통 예외, 설정 등
│   ├── domain // 핵심 비즈니스 규칙 정의
│   │   ├── model 
│   │   │   └── CartItem.java
│   │   └── repository // 저장소 계약
│   │       └── CartRepository.java
│   ├── infrastructure 
│   │   ├── adapter // port.out의 구현체 
│   │   └── persistence // JPA 기반 저장소 구현
│   │       ├── CartRepositoryAdapter.java 
│   │       └── jpa
│   │           └── CartJpaRepository.java
│   └── presentation // 외부 요청 진입점
│       └── web
│           └── CartController.java
└── GorogoroCartApplication.java
</code></pre>
<p>덕분에 이후 기능을 추가할 때 각 로직의 책임과 위치를 비교적 명확한 기준으로 판단할 수 있었습니다.</p>
<h3 id="흐름도">흐름도</h3>
<p>아래는 요청 흐름을 단순화한 다이어그램입니다.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/9157aadd-ffa2-4da7-a8cd-d8c3f8a5463c/image.png" alt=""></p>
<p>요청 흐름, 인증 흐름, 이벤트 흐름을 분리하여 설계했습니다.</p>
<ul>
<li>Gateway는 인증 판단과 라우팅만 담당하고,</li>
<li>Auth는 토큰 발급,</li>
<li>User는 사용자 상태의 단일 진실(Single Source of Truth)</li>
</ul>
<p>을 가지도록 역할을 나누었습니다. 다른 도메인은 이벤트를 통해 상태 변경을 반영하도록 설계했습니다.</p>
<p>이렇게 나누고 나니, Cart 도메인에서는 “인증을 어떻게 처리할지”를 거의 고민하지 않아도 되어 도메인 로직에 더 집중할 수 있었습니다.</p>
<h2 id="프로젝트-진행-과정">프로젝트 진행 과정</h2>
<h3 id="거대한-이벤트-스토밍">거대한 이벤트 스토밍</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/aea6c737-d157-4c65-8761-d2216cef581d/image.png" alt=""></p>
<p>이전까지는 이벤트 스토밍을 한 팀 단위로 진행하였지만, 항상 같은 도메인으로 이벤트 스토밍을 진행하다 보니 다른 관점으로 보는 것에 어려움이 있다고 생각했습니다. </p>
<p>그래서 저희는 프론트엔드와의 협업을 진행하기 전, 백엔드 두 팀의 협업 이벤트 스토밍을 진행하였습니다.</p>
<p>확실히 혼자 생각하고 진행할 때보다 모두가 생각하는 이벤트, 정책이 다르다는 것을 인지하고 이를 통해 생각하지 못했던 이벤트나 정책들이 떠오르는 경험을 할 수 있었습니다.</p>
<h3 id="두근두근-fe와의-만남">두근.두근 FE와의 만남</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/0da80691-4e1d-4ea0-a5b8-cbb875e14eab/image.png" alt=""></p>
<p>개발을 시작하기 전, 백엔드 팀에서 진행했던 이벤트 스토밍 결과를 공유하며 전체 흐름과 도메인 개념을 먼저 맞추는 시간을 가졌습니다.</p>
<p>이 과정에서 단순히 기능 목록을 전달하는 것이 아니라,
“이 기능은 어떤 상황에서 등장했고, 어떤 상태 변화를 만들고 싶은지”를 중심으로 설명했습니다.</p>
<p>그러다보니 자연스럽게 유비쿼터스 언어를 정리할 수 있었고, <strong>FE와 BE가 같은 단어를 같은 의미</strong>로 쓸 수 있었습니다.</p>
<p>저희의 협업 방식은 문서 중심이었습니다.
API 명세, 응답 예시, 상태 설명까지 최대한 상세하게 정리했고, 이 정도면 충분히 오해 없이 개발할 수 있겠다고 생각이 들었습니다.</p>
<p>하지만 실제로 API를 붙이는 단계에서 작은 이슈가 있었습니다.<br>명세가 잘못된 것은 아니었지만, 같은 응답을 두고 FE와 BE가 머릿속으로 그린 화면이 달랐습니다.</p>
<p>문서가 틀렸다기보다는, 문서가 모든 맥락을 대신해주지는 못한다는 걸 체감했습니다.</p>
<p>화면과 직접 맞닿는 API일수록 “이 응답을 어디에 쓰는지”, “이 필드가 바뀌면 화면에서 무엇이 달라지는지”까지 함께 이야기해야 한다는 것을 배울 수 있었습니다.</p>
<p><strong>문서는 협업의 출발점이지, 끝이 아니라는 것을 깨달았습니다.</strong></p>
<p>그 이후로는 API 하나를 정의하더라도 문서 + 짧은 대화 + 화면 기준 설명을 함께 가져가려 노력했고, 더 효율적인 협업을 할 수 있게 되었습니다.</p>
<h3 id="be-간의-협업-과정">BE 간의 협업 과정</h3>
<p>각 도메인을 완전히 분리된 레포지토리로 관리하면서 의존성을 낮출 수 있다는 장점이 있었습니다.
한 도메인의 변경이 다른 도메인에 직접적인 영향을 주지 않는 구조는 심리적인 안정감도 주었습니다.</p>
<p>하지만, 이 구조가 항상 편하기만 한 건 아니었습니다.
이벤트 처리나 다른 도메인의 상태를 참고해야 하는 상황에서는
전반적인 코드를 이해하는 데 시간이 조금 필요하다는 점도 있었습니다.</p>
<p>물론 코드 리뷰를 진행하면서 점점 다른 도메인에 대한 이해도 높아졌고, 협업 과정에서 다른 팀원분들이 적극적으로 리뷰를 해줘서 좋은 협업 분위기를 느낄 수 있었습니다.</p>
<p>초반에는 공통 컨벤션을 충분히 맞추지 않은 상태에서 개발이 진행되어 각 도메인마다 응답 포맷이 달랐었고, 프론트엔드 분들에게 혼란을 주는 일도 있었습니다.</p>
<p>이 경험을 통해, 기술적으로 분리된 구조일수록 초기 단계에서의 문서화와 컨벤션 합의가 훨씬 중요하다는 것을 체감할 수 있었습니다.</p>
<h2 id="고민했던-부분">고민했던 부분</h2>
<h3 id="domain-model-vs-jpa-entity-model">Domain Model vs. JPA Entity Model</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/6531fd7c-2ac2-42db-8b17-3ab2842541a3/image.png" alt=""></p>
<p>장바구니 도메인을 설계하면서 처음에는 도메인 모델과 JPA 엔티티 모델을 분리하여 관리하는 방식을 선택했습니다.</p>
<p>헥사고날 아키텍처를 적용한 만큼, 도메인이 인프라에 의존하지 않는 구조를 끝까지 가져가 보고 싶었기 때문입니다.</p>
<p>레이어드 구조였다면 JPA 엔티티를 그대로 도메인처럼 사용하는 선택도 가능했겠지만, 이번 프로젝트에서는 “도메인이 어떤 책임을 가져야 하는가”를 더 명확히 드러내고 싶었습니다.
그래서 저장 방식이나 ORM 제약보다, 도메인 행위와 규칙을 먼저 정의하는 방향으로 접근했습니다.</p>
<p>하지만 구현을 진행하면서, 이 선택이 현재 장바구니 도메인의 성격과는 맞지 않을 수 있지 않을까에 대한 고민이 들었습니다.
Cart 도메인에는 할인 정책이나 복잡한 비즈니스 규칙처럼 빈번하게 변경되거나 독립적으로 보호해야 할 정책이 존재하지 않았고, 대부분의 로직이 CRUD 성격의 단순한 상태 변경에 가까웠습니다.</p>
<p>그럼에도 도메인 모델을 기준으로 설계를 유지하다 보니,
아래와 같이 <strong>간단한 기능조차 조회 → 판단 → 변경의 흐름을 매번 명시적으로 풀어내야 했고,</strong>
그 과정에서 코드 양과 수정 포인트가 빠르게 늘어났습니다.</p>
<p>그래서 CartItem을 순수 도메인 모델로 유지하기보다는, JPA 엔티티 기반으로 사용하는 방향으로 설계를 변경하였습니다.</p>
<p>이 상황에서 도메인 분리는 설계를 보호하기보다는, 오히려 흐름을 복잡하게 만드는 요인이 될 수 있다고 느꼈습니다.</p>
<h4 id="변경-전">변경 전</h4>
<pre><code class="language-java">@Override
public void addCourse(AddCourseToCartCommand command) {
    Long userId = command.userId();

    List&lt;CartItem&gt; baselineItems = loadBaselineItems(userId);
    Cart targetCart = buildTargetCart(userId, baselineItems);
    targetCart.addCourse(command.courseId());

    cartRepository.save(targetCart, baselineItems);
}

@Override
public void clearCart(ClearCartCommand command) {
    Long userId = command.userId();
    // 불러오기
    List&lt;CartItem&gt; baselineItems = loadBaselineItems(userId);
    // 판단(비교)하기
    Cart targetCart = buildTargetCart(userId, baselineItems);
    targetCart.clear();
    // 저장하기
    cartRepository.save(targetCart, baselineItems);
}</code></pre>
<h4 id="변경-후">변경 후</h4>
<pre><code class="language-java">@Override
public void addCourse(AddCourseToCartCommand command) {
    ...
    CartItem newItem = CartItem.of(command.userId(),command.courseId(), Instant.now());
    cartRepository.save(newItem);
}

@Override
public void clearCart(ClearCartCommand command) {
    cartRepository.deleteAllByUserId(command.userId());
}</code></pre>
<blockquote>
<p>도메인 규칙이 복잡하고 자주 변경된다면 나누는 것이 좋다. 예를 들어 이커머스의 각종 할인 정책(쿠폰, 마일리지 등)은 복잡하고 변경이 잦다. 이런 도메인은 가능한 한 순수하게 유지하여, 테스트 가능성(testability)을 극대화하고 리팩터링을 통해 쉽게 구조를 변경할 수 있도록 해야 한다.</p>
</blockquote>
<p>이런 고민 과정을 통해 도메인 분리는 <strong>‘무조건 적용해야 할 원칙’</strong>이 아니라,
도메인의 복잡도와 변경 가능성, 그리고 팀의 설계 이해도에 따라 선택되어야 한다는 기준을 갖게 되었습니다.</p>
<h3 id="api-호출-동기-비동기">API 호출 동기? 비동기?</h3>
<p>장바구니 구현 과정에서 또 하나 고민했던 지점은 강의 정보 조회를 어떤 방식으로 처리할 것인가였습니다.</p>
<p>장바구니에는 강의 ID만 저장되어 있었고,
화면에 필요한 강의 정보(제목, 썸네일, 가격 등)는 Course 도메인에서 가져와야 했습니다.</p>
<p>이때 두 가지 선택지가 있었습니다.</p>
<ul>
<li>장바구니에 필요한 강의 정보를 저장하고, 비동기 이벤트로 갱신할 것인가</li>
<li>요청 시점마다 Course 도메인에 동기 API를 호출할 것인가</li>
</ul>
<p>이 고민은 호출 방식 자체보다는 <strong>“장바구니가 강의 정보를 소유해야 하는가”</strong>였습니다.</p>
<p>비동기 방식이라면 호출 비용을 줄이고 조회 성능을 개선할 수 있었지만,
장바구니는 사용자가 담은 항목을 관리하는 도메인이지
강의의 상태나 정보를 책임지는 도메인은 아니라고 판단했습니다.</p>
<p>강의 정보의 최신성 역시 Course 도메인의 책임에 가깝다고 보았고,
장바구니가 이를 내부에 저장하는 순간 도메인 간 책임 경계가 흐려질 수 있다고 느꼈습니다.</p>
<p>그래서 강의 정보는 Course 도메인이 소유하고,
장바구니는 조회 시점에 동기 API로 참조하는 방식을 선택했습니다.</p>
<p>이 선택은 호출 횟수나 성능 측면에서는 부담이 될 수 있지만, 데이터의 최신성을 보장할 수 있고
각 도메인이 무엇을 책임지는지가 명확해진다는 장점이 있었습니다.</p>
<p>이번 고민을 통해 비동기 처리가 항상 더 나은 해법은 아니며,
도메인이 무엇을 ‘기억하느냐’보다 무엇을 ‘소유하느냐’가 더 중요하다는 기준을 세울 수 있었습니다.</p>
<h2 id="마무리하며">마무리하며,,</h2>
<p>글을 마무리하기 전에, 프로젝트 시작 전 세웠던 목표들을 다시 한 번 꺼내 봤습니다.</p>
<h4 id="1-어그리게이트-경계-잡는-연습-더-해보기">1. 어그리게이트 경계 잡는 연습 더 해보기</h4>
<p>Cart 도메인을 단일 책임으로 가져가며, “이 기능이 정말 Cart의 책임인가?”를 계속 질문할 수 있었습니다. 완벽하다고 말하긴 어렵지만 이전보다 경계를 의식하며 설계했다는 점에서 의미있었습니다.</p>
<h4 id="2-조회-모델-설계-연습-dto--projection--read-model">2. 조회 모델 설계 연습 (DTO / Projection / Read Model)</h4>
<p>조회 흐름을 Command와 분리하고, 화면 단위로 필요한 데이터가 무엇인지 고민하는 경험을 할 수 있었습니다. 하지만, Projection과 Read Model을 더 적극적으로 활용하지 못한 아쉬움은 있습니다.</p>
<h4 id="3-이-화면을-위한-read-model은-무엇인가를-먼저-고민하기">3. “이 화면을 위한 Read Model은 무엇인가?”를 먼저 고민하기</h4>
<p>FE 협업 과정에서 이 질문이 자연스럽게 등장했고, API를 기능 기준이 아니라 화면 기준으로 바라보게 되었습니다.</p>
<h4 id="4-이해가-막히면-설계-단계에서-질문하기">4. 이해가 막히면 설계 단계에서 질문하기</h4>
<p>이벤트 스토밍, 협업 미팅, 코드 리뷰 과정에서 이전보다 훨씬 빠르게 질문하고 의견을 나눌 수 있었습니다.</p>
<h4 id="5-작은-단위로-자주-pr-보내기">5. 작은 단위로 자주 PR 보내기</h4>
<p>도메인 단위로 쪼개서 PR을 보내는 습관을 유지하려 노력했고, 리뷰를 통해 설계에 대한 피드백을 빠르게 받을 수 있었습니다.</p>
<p>하지만, &quot;자주&quot;는 아니었던 것 같습니다. 한 기능을 작업하면서 구현 방식이 틀린 방향은 아닌지에 대한 걱정에 재확인하는 과정에서 구현 시간을 많이 잡아먹었던 것 같습니다.</p>
<h4 id="6-도메인-시나리오를-설명하는-테스트-쌓기">6. 도메인 시나리오를 설명하는 테스트 쌓기</h4>
<p>일부 시도는 했지만, “설명을 위한 테스트”까지는 아직 충분히 쌓지 못했다는 아쉬움이 있습니다.</p>
<p>전체적으로 보면, 모든 목표를 완벽하게 달성했다기보다는 어디까지 고민해봤고, 어디가 아직 부족한지 명확해졌다는 점이 이번 프로젝트를 진행하면서 가장 크게 배운 점이라고 생각합니다.</p>
<h2 id="다음-프로젝트를-위한-목표">다음 프로젝트를 위한 목표</h2>
<ul>
<li>헥사고날 구조를 ‘지키는 것’보다 ‘언제 느슨하게 가져갈지 판단하는 연습’ 해보기</li>
<li>Projection / Read Model을 실제로 활용해 보는 경험 쌓기</li>
<li>API 설계 시 문서 중심에서 한 단계 더 나아가, 늘 더 나은 협업 방식 고민하기</li>
<li>도메인 시나리오를 설명하는 테스트를 “나를 위한 테스트”가 아니라 “동료에게 설명할 수 있는 테스트”로 작성해보기</li>
</ul>
<p>설계도 협업도, 
결국 정답을 맞히는 일이 아니라 
계속 질문하고 조정해 나가는 과정이라는 걸 배울 수 있었습니다.</p>
<blockquote>
<p>달라도 괜찮았고, 그래서 더 많이 배울 수 있었다.</p>
</blockquote>
<p>긴 글 읽어주셔서 감사합니다 ☺️</p>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://herbertograca.com/2017/11/16/explicit-architecture-01-ddd-hexagonal-onion-clean-cqrs-how-i-put-it-all-together/">헥사고날 아키텍처</a></li>
<li><a href="https://medium.com/@jazzbach/%EB%8F%84%EB%A9%94%EC%9D%B8-%EB%AA%A8%EB%8D%B8%EA%B3%BC-jpa-e1d602b4a526">도메인 모델과 영속 모델</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LXP] roadmap.sh 써보셨나요?]]></title>
            <link>https://velog.io/@dd-jiyuni/LXP-roadmap.sh-%EC%8D%A8%EB%B3%B4%EC%85%A8%EB%82%98%EC%9A%94</link>
            <guid>https://velog.io/@dd-jiyuni/LXP-roadmap.sh-%EC%8D%A8%EB%B3%B4%EC%85%A8%EB%82%98%EC%9A%94</guid>
            <pubDate>Sun, 23 Nov 2025 14:38:57 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며..</h1>
<p><a href="https://velog.io/@dd-jiyuni/wanted-project-01">이전</a> 회고에 이어 포텐업에서의 2개월 LXP 프로젝트를 새로운 팀원들과 시작하게 되었습니다.</p>
<p>이번 프로젝트에서는 처음으로 DDD(Domain-Driven Design)를 적극적으로 도입해 봤습니다.
그 과정에서 <strong>무엇을 배웠고, 무엇이 아쉬웠고, 다음에는 무엇을 바꾸고 싶은지</strong>를 정리했습니다.</p>
<hr>
<p>회고를 시작하기 앞서 먼저 프로젝트를 소개드리겠습니다.</p>
<h1 id="프로젝트-소개">프로젝트 소개</h1>
<blockquote>
<p><a href="https://github.com/potenup-kllhy/roadmap/tree/develop">Github 보러가기</a></p>
</blockquote>
<h2 id="협업-방식">협업 방식</h2>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/180e0c3f-8e62-4a16-bf41-ad2ce08c8d1f/image.png" alt=""></p>
<p>Notion과 GitHub 등을 활용했지만, 핵심은 도구 자체가 아닌 <strong>&quot;서로의 성장을 위한 기록과 리뷰 문화&quot;</strong>였습니다.</p>
<ul>
<li><strong>Daily Scrum</strong>: 이미 한 일, 할 일, 그리고 장애 요소를 매일 공유했습니다.</li>
<li><strong>Log &amp; Reference</strong>: 논의 내용과 결정 사항을 Notion에 남기며, 모두가 볼 수 있는 레퍼런스로 만들었습니다.</li>
<li><strong>Discussion</strong>: 학습 내용과 고민 지점을 Discussion에 공유하며, 인사이트를 확장할 수 있었습니다.</li>
<li><strong>Spotless</strong>: 코드 포맷팅을 자동화하여 컨벤션 논쟁을 최소화하고, 도메인에 집중할 수 있었습니다.</li>
</ul>
<h2 id="구현-방식">구현 방식</h2>
<p>기능 구현에 바로 들어가기 전, 이벤트 스토밍을 통해 도메인 이벤트를 도출하고 이를 기준으로 도메인을 나눴습니다.</p>
<p>이후 각 도메인을 적절히 분배하여 담당자를 정하고, DDD 스타일의 패키지 구조와 레이어를 적용해 구현을 진행했습니다.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/1701cd3a-dc0f-449e-93b1-18a10f4f716e/image.png" alt=""></p>
<h3 id="가장-큰-차이점--lms-vs-lxp">가장 큰 차이점 : LMS vs LXP</h3>
<table>
<thead>
<tr>
<th>구분</th>
<th>LMS (이전 프로젝트)</th>
<th>LXP (이번 프로젝트)</th>
</tr>
</thead>
<tbody><tr>
<td>핵심</td>
<td>관리자 중심 (CRUD)</td>
<td>사용자 경험 중심 (Customizing)</td>
</tr>
<tr>
<td>목표</td>
<td>Spring MVC 경험</td>
<td>도메인 주도 설계 (DDD)</td>
</tr>
<tr>
<td>레퍼런스</td>
<td>기존 강의 사이트</td>
<td><a href="https://roadmap.sh/backend">roadmap.sh</a></td>
</tr>
</tbody></table>
<p>단순히 화면을 그리는 것이 아니라, <strong>&quot;사용자가 학습 경로를 스스로 커스터마이징한다&quot;</strong>는 도메인적 특성을 살리기 위해 DDD를 선택했습니다.</p>
<h1 id="ddd">DDD?</h1>
<blockquote>
<p>DDD는 “Domain Driven Design”의 약자로, 복잡한 도메인 모델을 설계하고 구현하기 위한 방법론이다. 도메인 전문가와 개발자가 협력해 도메인 모델을 정의하고, 이를 기반으로 소프트웨어를 설계하는 것을 목표로 한다.</p>
</blockquote>
<p>처음에는 DDD가 MVC와 완전히 다른 “어떤 패턴”이라고 생각했습니다.
실제로 써보니, DDD는 특정 프레임워크나 패턴이라기보다 도메인 중심으로 사고하고 설계하는 방법론에 가까웠습니다.</p>
<p>그래서</p>
<ul>
<li>DDD를 사용한다고 해서 MVC를 못 쓰는 것도 아니고,</li>
<li>MVC를 쓴다고 해서 DDD를 못 하는 것도 아니라는 걸 알게 됐습니다.</li>
</ul>
<p>MVC 위에 DDD의 관점을 얹어서, “코드를 어떤 책임 단위로 나눌지 도메인을 어디까지 보호할지”를 고민해 보는 쪽에 더 가깝다고 느꼈습니다.</p>
<hr>
<p>그렇다면 왜 DDD여야 했을까요 🤔?</p>
<h2 id="왜-ddd인가">왜 DDD인가?</h2>
<p>이번 프로젝트에서 <strong>“왜 굳이 DDD여야 했는가?”</strong> 를 계속 질문하게 됐습니다. 
단순히 유행하는 개념을 써보고 싶어서가 아니라, 
LXP라는 도메인의 특성과 팀의 협업 방식, 그리고 개인적인 학습 목표가 맞물린 선택에 가까웠습니다.</p>
<h4 id="정리하면">정리하면,</h4>
<ul>
<li>LXP 도메인 자체가 복잡해서, 화면이 아니라 도메인부터 다시 설계할 필요가 있었습니다.</li>
<li>같은 화면을 보면서도 <strong>서로 다른 언어</strong>를 쓰는 문제를 해결하고 싶었습니다.</li>
<li>설계–구현–코드 리뷰 <strong>전체를 도메인 중심으로 엮어 보는</strong> 실험을 해보고 싶었습니다.</li>
<li>말로만 듣던 DDD를 실제 프로젝트에 적용해 보며 <strong>직접 몸으로 이해해보고 싶었습니다.</strong></li>
</ul>
<h3 id="1-lxp-도메인이-던진-질문">1. LXP 도메인이 던진 질문</h3>
<p>이번 프로젝트의 목표는 단순히 강좌와 강의를 CRUD로 관리하는 LMS가 아니라, 사용자 중심으로 학습 경험을 설계하는 LXP였습니다.</p>
<p>그래서,</p>
<ul>
<li>“어떤 강의를 가지고 있느냐”보다</li>
<li>“사용자가 어떤 경로로, 어떤 맥락에서 학습하느냐”</li>
</ul>
<p>가 더 중요해진다고 느꼈습니다.</p>
<p>이 의미를 생각하다 보니 이런 질문들을 던지게 됐습니다.</p>
<ul>
<li>“이걸 화면 기준이 아니라, 도메인 모델 기준으로 다시 잡아보면 어떨까?”</li>
<li>“사용자, 로드맵, 토픽, 별점 같은 개념들을 
단순 테이블이 아니라 규칙과 책임을 가진 도메인으로 바라보면 어떨까?”</li>
</ul>
<p>이 지점에서 “도메인부터 먼저 잡고 가는 설계 방식”인 DDD를 떠올리게 되었고,<br>단순히 MVC를 써보는 수준을 넘어 <strong>도메인 중심 설계를 직접 경험해 보고 싶다</strong>는 생각이 들었습니다.</p>
<blockquote>
<h3 id="💡-요약">💡 요약</h3>
<p>LXP라는 도메인 자체가 “학습 경험”에 초점이 있다 보니,
자연스럽게 화면이 아닌 도메인 모델부터 설계하고 싶은 마음이 들었습니다.</p>
</blockquote>
<h3 id="2-같은-화면-다른-언어를-맞추기-위해">2. 같은 화면, 다른 언어를 맞추기 위해</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/b23ccc95-8146-4df9-b61e-5c633a5d8e21/image.png" alt=""></p>
<blockquote>
<p>이벤트 스토밍은 실제로 이렇게 포스트잇을 잔뜩 붙여가며 진행했어요 🙌</p>
</blockquote>
<p>roadmap.sh를 분석한 뒤 이벤트 스토밍을 진행하면서, 
같은 화면을 보고도 팀원마다 쓰는 언어가 다르다는 걸 체감했습니다. </p>
<ul>
<li>한 팀원은 Skill·Role 같은 사이드 요소를 카테고리로 보았고,</li>
<li>또 다른 사람은 각 로드맵의 루트(예: Backend)를 카테고리로 이해했습니다.</li>
<li>로드맵에서 무엇을 Topic, 무엇을 SubTopic으로 볼지도 엇갈렸습니다.</li>
</ul>
<p>이 경험을 통해 <strong>유비쿼터스 언어의 필요성</strong>을 느꼈습니다.
이벤트 스토밍에서 도출한 도메인 이벤트를 기반으로 용어를 정리하면서,
바운디드 컨텍스트와 어그리게이트를 나눌 때 DDD의 관점을 적극적으로 활용하게 되었습니다.</p>
<blockquote>
<ul>
<li><strong>바운디드 컨텍스트(Bounded Context)</strong> : “이 말은 여기 안에서만 이 의미로 쓴다”라고 경계를 그어놓은 도메인 구역.</li>
<li><strong>어그리게이트(Aggregate)</strong> : “여기까지는 한 번에 함께 일관성을 지켜야 하는 덩어리”를 정의한 것.</li>
</ul>
</blockquote>
<h3 id="3-도메인-중심으로-엮어보기">3. 도메인 중심으로 엮어보기</h3>
<p>이전 프로젝트에서도 요구사항 정의와 ERD 설계는 했지만, </p>
<p>이번에는 여기에 더해</p>
<ul>
<li>UML,</li>
<li>유비쿼터스 언어 정의,</li>
<li>바운디드 컨텍스트, 어그리게이트 설계</li>
</ul>
<p>까지 포함해 <strong>설계 단계에 더 많은 시간</strong>을 투자했습니다.</p>
<p>동시에</p>
<ul>
<li>작은 PR 단위,</li>
<li>커밋 메시지 컨벤션,</li>
<li>상호 코드 리뷰 규칙</li>
</ul>
<p>을 세우면서 단순 스타일이나 구현 디테일이 아니라, 
<strong><em>“이 도메인 규칙을 이 위치에서 책임지는 게 맞는가?”</em></strong>라는 관점으로 이야기를 나눌 수 있었습니다. </p>
<blockquote>
<h4 id="💡-요약-1">💡 요약</h4>
<p>설계–구현–코드 리뷰를 하나의 흐름으로 엮어 본 첫 실험이었고,
“도메인 관점에서 코드를 보는 연습”이 많이 되었습니다.</p>
</blockquote>
<h3 id="4-말로만-듣던-ddd">4. 말로만 듣던 DDD</h3>
<p>강의와 글로만 접하던 DDD를 “알고 있다” 수준에 두기보다는, 실제 프로젝트에 적용해 보면서 <strong>어디까지 도움이 되고 어디서부터는 과한지</strong> 직접 느껴보고 싶었습니다.</p>
<p>이번 프로젝트에서는 DDD를 이론이 아니라 코드와 협업 경험으로 이해해보고 싶었고, 덕분에 도메인 중심 사고와 설계가 무엇인지 조금은 느낄 수 있었습니다.</p>
<hr>
<p>이제부터는, 이렇게 선택한 DDD 관점 아래에서 <strong>실제 구현을 하며 부딪혔던 고민들</strong>을 정리해보려 합니다.</p>
<h1 id="고민포인트">고민포인트</h1>
<p>발표 이후 강사님께 아래와 같은 피드백을 들었습니다.</p>
<blockquote>
<p>MVP 단계에서는 SubTopic이 Topic 하위에 포함되는 구조로 가도 괜찮지만,
서비스가 확장되면 강사라는 액터가 등장해 콘텐츠를 직접 업로드하게 될 수 있다.
그런 경우를 미리 염두에 뒀다면 SubTopic을 루트 어그리게이트로 설계하는 선택도 가능했을 것.</p>
</blockquote>
<p>이 피드백을 듣고, 제가 맡았던 도메인에서의 고민 포인트와 연결점이 있지 않을까 생각하게 되었고,</p>
<p><strong>“도메인을 어디까지 확장 가능성을 염두에 두고 설계할 것인가”</strong>를 다시 돌아보는 계기가 되었습니다.</p>
<h2 id="1-도메인-확장성을-어디까지-설계에-녹일-것인가">1. 도메인 확장성을 어디까지 설계에 녹일 것인가</h2>
<p>제가 맡았던 도메인은 <strong>Category(카테고리)</strong>와 <strong>Star(별점)</strong>이었습니다.</p>
<p>둘 다 Roadmap과 분리된 <strong>별도의 어그리게이트</strong>로 설계했습니다.</p>
<ul>
<li>Category → 로드맵을 분류하는 카테고리 도메인</li>
<li>Star → 사용자가 로드맵에 남기는 평가 도메인</li>
</ul>
<p>으로 보고, 각자 독립된 도메인 모델과 패키지로 가져갔습니다.</p>
<p>이미 어그리게이트로 분리한 상태였지만, 구현을 진행할수록 
<strong>“어디까지 확장 가능성을 설계에 녹여 둘 것인가”</strong> 에 대한 고민이 깊어졌습니다.</p>
<h3 id="1-1-star-도메인-이미-어그리게이트지만-어떻게-확장할-것인가">1-1. Star 도메인: 이미 어그리게이트지만, 어떻게 확장할 것인가</h3>
<h3 id="상황">상황</h3>
<p>Star는 처음부터 <strong>Roadmap 내부의 필드가 아닌 독립 어그리게이트</strong>였습니다.</p>
<ul>
<li>하나의 Star는 <code>user</code>와 <code>roadmap</code>을 연결하는 평가이고,<code>value</code>는 1~5점 사이의 값입니다.</li>
<li>“한 유저가 한 타깃에 대해 한 번만 평가할 수 있다”는 불변식을 가지고 있습니다.</li>
</ul>
<p>그래서 처음에는 아래처럼, Star 테이블을 떠올렸습니다.</p>
<pre><code class="language-java">// &quot;한 유저가 한 로드맵에 한 번만 평가할 수 있다&quot; 불변식을 가진 초기 설계
@Entity
@Table(
    name = &quot;star_road_map&quot;,
    uniqueConstraints = {
        @UniqueConstraint(
            name = &quot;uk_star_user_roadmap&quot;,
            columnNames = {&quot;user_id&quot;, &quot;road_map_id&quot;}
        )
    }
)
public class StarRoadMap {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = &quot;value&quot;, nullable = false)
    private int value;        // 1~5점

    @Column(name = &quot;user_id&quot;, nullable = false)
    private Long userId;

    @Column(name = &quot;road_map_id&quot;, nullable = false)
    private Long roadMapId;

    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;

    // 생성자/검증 로직 등 생략
}</code></pre>
<h3 id="고민">고민</h3>
<p>이 코드만 보면 도메인 규칙이 꽤 명확하게 드러납니다.</p>
<ul>
<li>하나의 Star는 userId + roadMapId 조합으로 유일하다.
→ “한 유저는 한 로드맵을 한 번만 평가한다.”</li>
<li>Star는 Roadmap의 필드가 아니라, 별도 테이블/애그리게이트로 분리되어 있다.</li>
</ul>
<p>하지만 구현을 진행할수록 이런 시나리오들이 떠올랐습니다.</p>
<ul>
<li>“별점이 Roadmap에만 머무르지 않고 Topic / SubTopic에도 붙게 된다면?”</li>
<li>“나중에 강사, 강의, 강사 프로필에도 같은 방식의 평가를 쓰고 싶다면?”</li>
</ul>
<p>이 지점에서 <strong>“독립된 애그리게이트로 분리했다는 사실만으로 충분한가?”</strong> 라는 질문이 생겼습니다.</p>
<p>Roadmap에서만 쓸 생각으로 road_map_id에 고정해 버리면,
나중에 다른 도메인에 재사용하려 할 때 모델 자체를 갈아엎어야 하는 구조가 되기 때문입니다.</p>
<h3 id="지금-정리한-기준">지금 정리한 기준</h3>
<ul>
<li><p>하나의 유저는 하나의 타깃에 대해 한 번만 평가할 수 있다.
→ (user_id + target_id) 유니크 불변식</p>
</li>
<li><p>Star는 Roadmap에만 붙는 개념이 아니라, Topic / SubTopic / Instructor 등 
여러 타깃에 붙을 수 있는 &quot;범용 평가 도메인&quot;이 될 수 있다.</p>
</li>
<li><p>지금은 Roadmap에만 사용하더라도, 
나중을 생각하면 <code>(target_type + target_id)</code> 구조로 여지를 열어두는 편이 확장에 유리하다.</p>
</li>
</ul>
<h4 id="이-기준으로-옮기면">이 기준으로 옮기면?</h4>
<pre><code class="language-java">// 여러 도메인에 붙을 수 있는 &quot;평가 타깃&quot; 값 객체
@Embeddable
public class StarTarget {

    @Enumerated(EnumType.STRING)
    @Column(name = &quot;target_type&quot;, nullable = false, length = 50)
    private StarTargetType type; // ex) ROADMAP, TOPIC, SUB_TOPIC

    @Column(name = &quot;target_id&quot;, nullable = false)
    private Long id;

    ...
}</code></pre>
<pre><code class="language-java">@Entity
@Table(
    name = &quot;stars&quot;,
    uniqueConstraints = {
        @UniqueConstraint(
            name = &quot;uk_star_user_target&quot;,
            columnNames = {&quot;user_id&quot;, &quot;target_type&quot;, &quot;target_id&quot;}
        )
    }
)
public class Star {
   ...
}</code></pre>
<p>지금 프로젝트에서는 여기까지 구현하진 않았지만,</p>
<ul>
<li>현재 코드(StarRoadMap) 가 보장하는 불변식은 무엇인지</li>
<li>확장 가능성을 의식한 코드 스케치(Star + StarTarget) 는 어떤 차이가 있는지</li>
</ul>
<p>를 나란히 두고 보면서</p>
<p><strong>“우리는 이미 Star를 애그리게이트로 분리했지만, 도메인 관점에서도 ‘확장’을 염두에 둔 설계였는가?”</strong></p>
<p>를 다시 한 번 질문해 볼 수 있었습니다.</p>
<h3 id="1-2-category-도메인-지금은-단순하지만-나중에-달라질-수-있는-도메인">1-2. Category 도메인: 지금은 단순하지만, 나중에 달라질 수 있는 도메인</h3>
<h3 id="상황-1">상황</h3>
<p>Category도 Roadmap과 분리된 <strong>독립 어그리게이트</strong>로 가져갔습니다.</p>
<ul>
<li>Category → 로드맵을 묶어주는 “분류 도메인”</li>
<li>Roadmap → 특정 Category에 속함</li>
</ul>
<p>이라는 단순한 관계로 출발했고, 이에 맞게 엔티티도 아주 단순한 형태입니다.</p>
<pre><code class="language-java">@Entity
@Table(name = &quot;category&quot;)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Category extends IdEntity {

    @Enumerated(EnumType.STRING)
    private CategoryType categoryType; // 예: ROLE, SKILL로 고정된 분류 타입

    @Column(nullable = false)
    private String name;               
}</code></pre>
<p>이 코드만 놓고 보면 Category는</p>
<ul>
<li>고정된 CategoryType 값에 의해 구분되고,</li>
<li>별도의 parent/owner 같은 개념이 없는,</li>
<li>전역적으로 공유되는 Flat한 카테고리 목록</li>
</ul>
<p>관리자가 미리 정의해 둔 카테고리 집합에서 로드맵이 하나를 선택해서 매핑된다는 설계에 잘 맞는 구현입니다.</p>
<h3 id="고민-1">고민</h3>
<p>지금 단계(LXP MVP)에서는 <strong>“최소한의 Category 어그리게이트”</strong>로 적절한 선택이었습니다.</p>
<p>LXP의 특성상, 구현 이후 이런 생각들이 따라왔습니다.</p>
<ul>
<li>사용자가 직접 <strong>커스텀 카테고리</strong>를 만들 수 있게 된다면?</li>
<li>카테고리가 단일 depth가 아니라 상위/하위 관계를 가진 <strong>트리 구조</strong>로 바뀐다면?</li>
</ul>
<p>이미 Category를 어그리게이트로 분리해 두었기 때문에
“나중에 이 도메인의 라이프사이클이 이렇게까지 바뀔 수도 있겠다”를 더 고민해 볼 수 있었습니다.</p>
<h3 id="지금-정리한-기준-1">지금 정리한 기준</h3>
<p>카테고리 도메인을 정리하면서 스스로 세운 기준은 대략 이런 느낌이었습니다.</p>
<ul>
<li>지금은 &quot;전역, 고정, 단일 depth&quot; 카테고리로도 충분하다.</li>
<li>하지만 LXP 특성상 &quot;유저 커스텀&quot;이나 &quot;계층형 트리&quot;로 확장될 가능성이 높다.</li>
<li>그렇다면 Category는 &quot;변화할 가능성이 큰 도메인&quot;이라는 메모를 해두는 편이 좋다.</li>
</ul>
<h4 id="만약-트리-구조를-확장한다면">만약 트리 구조를 확장한다면?</h4>
<pre><code class="language-java">@Entity
@Table(name = &quot;category&quot;)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Category extends IdEntity {

    @Enumerated(EnumType.STRING)
    private CategoryType categoryType;

    @Column(nullable = false)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;parent_id&quot;)
    private Category parent;        // 상위 카테고리

    @OneToMany(mappedBy = &quot;parent&quot;)
    private List&lt;Category&gt; children = new ArrayList&lt;&gt;(); // 하위 카테고리 리스트

    ...
}</code></pre>
<p>그리고 유저 커스텀 카테고리까지 상상한다면,
전역 Category와는 별도 어그리게이트로 이렇게 분리하는 선택도 가능할 것 같습니다.</p>
<pre><code class="language-java">// 전역 Category는 그대로 두고, 사용자 전용 커스텀 카테고리는 별도 애그리게이트로 분리
@Entity
@Table(name = &quot;user_category&quot;)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class UserCategory extends IdEntity {

    @Column(nullable = false)
    private Long userId;       // 카테고리를 만든 사용자

    @Column(nullable = false)
    private String name;       // 사용자가 지정한 커스텀 이름

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = &quot;base_category_id&quot;)
    private Category base;    
}</code></pre>
<p>지금 프로젝트에서 여기까지 구현한 것은 아니지만,</p>
<ul>
<li>현재 코드(Category)는 <strong>“전역, Flat, 단일 depth 카테고리”</strong>라는 현재 요구사항에 잘 맞고,</li>
<li>트리/커스텀 같은 요구사항이 붙는다면 어느 지점을 바꿔야 할지 코드만 봐도 상상할 수 있게 된다는 점에서,</li>
</ul>
<p>“지금은 리스트처럼 보이는 도메인도, 나중에는 구조와 책임이 달라질 수 있다.”는 걸 더 민감하게 느끼게 해 준 도메인이었습니다.</p>
<p>그래서 다음에는 설계 단계에서부터</p>
<ul>
<li>“이 어그리게이트는 나중에 depth나 owner가 붙을 가능성이 있는가?”</li>
<li>“그렇다면 지금은 최소 구현으로 두더라도, 어디까지를 이 도메인의 책임으로 볼 것인가?”</li>
</ul>
<p>를 먼저 적어두고 시작해 보자는 기준을 갖게 되었습니다.</p>
<hr>
<p>도메인 자체에 대한 고민과 함께, 이번 프로젝트에서는 <strong>협업 과정에서의 저 자신의 태도</strong>에 대해서도 돌아보게 되었습니다.</p>
<h2 id="2-질문-pr-테스트의-타이밍을-어떻게-가져갈-것인가">2. 질문, PR, 테스트의 타이밍을 어떻게 가져갈 것인가</h2>
<p>프로젝트를 진행하면서 아래 네 가지가 계속 마음에 남았습니다.</p>
<ul>
<li>질문을 적극적으로 하지 못했던 부분</li>
<li>정한 기간이 있었는데 PR 리뷰나 머지를 지키지 못한 부분</li>
<li>너무 급하게 테스트를 찍어내다 보니 목데이터를 제대로 신경 쓰지 못한 부분</li>
<li>설계에 충분히 참여하지 못한 채 개발을 시작해서 막막했던 순간들</li>
</ul>
<p>이걸 <strong>Before / After</strong>로 나눠 보니 더 선명해졌습니다.</p>
<h3 id="before--after-정리">Before / After 정리</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td>질문</td>
<td>“조금만 더 고민해보고 그래도 안 되면 물어보자”</td>
<td>설계 단계에서 이해 안 되는 부분은 바로 질문하고, 논의에 적극적으로 참여하기</td>
</tr>
<tr>
<td>PR</td>
<td>한 번에 몰아서, 덩치 큰 PR</td>
<td>작은 단위로 자주 올리고, <strong>중간 진행 상황 공유용 도구</strong>로 PR을 인식</td>
</tr>
<tr>
<td>테스트</td>
<td>“일단 통과하는 테스트” 위주, 목데이터는 대충</td>
<td>도메인 시나리오를 설명하는 문서에 가깝게 보고, 실제 도메인을 대표하는 목데이터를 고민</td>
</tr>
</tbody></table>
<p>그리고 중간중간 받았던 팀원분들의 이해도 체크와 조언들이 큰 도움이 되었습니다 🙇🏻‍♀️
지금 이 설계를 어떻게 이해하고 있는지와 같은 팀원들의 질문이 오히려 제 사고를 정리하게 만든 계기가 되었습니다.</p>
<p><em><strong>다시 한 번 팀원분들에게 너무 감사드립니다 😊</strong></em></p>
<h1 id="학습-포인트">학습 포인트</h1>
<h2 id="1-도메인-이벤트와-모듈-간-통신">1. 도메인 이벤트와 모듈 간 통신</h2>
<p>도메인별 모듈이 생기면서, <strong>모듈 간 통신 방식</strong>도 함께 고민하게 되었습니다.</p>
<ul>
<li>같은 모듈 내부에서는<br><code>presentation → application → domain → infra</code> 방향의 의존을 유지하고,</li>
<li>다른 모듈과는 <strong>직접 참조를 최소화하면서 어떻게 연결할지</strong>가 관건이었습니다.</li>
</ul>
<p>제가 맡았던 Category/Star 도메인은 <strong>Roadmap 도메인의 상태 변화</strong>에 영향을 받는 쪽(consumer)에 더 가까웠습니다.</p>
<p>예를 들어,</p>
<ul>
<li>로드맵의 공개 상태가 변경될 때,</li>
<li>로드맵이 더 이상 노출되지 않는 상태가 될 때,</li>
</ul>
<p>기존이라면 그냥 Roadmap 서비스를 참조해 와서,</p>
<ul>
<li>로드맵 상태를 조회해서 Star/Category 쪽 상태를 맞추자.</li>
<li>필요하면 로드맵 쪽 메서드를 직접 호출해서 정합성을 맞추자.</li>
</ul>
<p>하지만 모듈별로 나뉜 구조와 DDD 관점을 함께 보면서, 한 번 더 이런 질문을 던지게 되었습니다.</p>
<ul>
<li><p>Star/Category가 정말 Roadmap 서비스에 <strong>직접 의존해야 하는가?</strong></p>
</li>
<li><p>아니면 “로드맵이 생성되었다”, “로드맵이 비공개로 전환되었다” 같은 </p>
</li>
<li><p><em>로드맵 도메인 이벤트를 수신하는 쪽*</em>으로 두는 게 더 자연스러운가?</p>
</li>
</ul>
<p>이번 프로젝트에서는 모든 흐름을 완전히 이벤트 기반으로 만들지는 못했지만,</p>
<p>실제로 Star 모듈이 다른 도메인의 <strong>이벤트를 구독해서 반응하는 구조</strong>는 일부 구현해 볼 수 있었습니다.</p>
<p>예를 들어, Star 모듈에서는 다음과 같이</p>
<ul>
<li>유저 계정 상태 변경 이벤트(<code>UserAccountStatusUpdated</code>)</li>
<li>로드맵 도메인 이벤트(<code>RoadMapEventOccurred</code>)</li>
</ul>
<p>를 구독해서 별점 정보를 정리합니다.</p>
<pre><code class="language-java">@Slf4j
@Component
@RequiredArgsConstructor
public class StarRoadMapEventHandler {

    private final StarRoadMapCommandService starRoadMapCommandService;
    private final RoadMapQueryService roadMapQueryService;

    // 유저 상태 변경 이벤트는 별도 정리 (예: BLOCKED → 별점 삭제)
    // ...

    /**
     * 로드맵 도메인에서 &quot;삭제됨&quot; 이벤트가 발생하면
     * → 그 로드맵에 달린 별점들을 정리한다.
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void handlerRoadMapEventOccurred(RoadMapEventOccurred event) {
        if (event.eventType() == EventType.DELETED) {
            log.info(&quot;RoadMap deleted → delete stars, roadmapId={}&quot;, event.roadMapId());

            // 실제 코드에서는 삭제 가능 상태 체크를 위해 RoadMapView 를 한 번 더 확인
            RoadMapView roadMapView = roadMapQueryService.findById(event.roadMapId());
            starRoadMapCommandService.deleteAllStarByRoadMapId(roadMapView.id());
        }
    }
}</code></pre>
<p>이 코드 덕분에 Star 모듈은</p>
<ul>
<li><code>UserService</code> 나 <code>RoadMapService</code> 에 직접 의존하지 않고,</li>
<li><strong>&quot;유저가 BLOCKED 되었다&quot;</strong>, <strong>&quot;로드맵이 삭제되었다&quot;</strong> 라는 <em>도메인 이벤트</em>만 보고</li>
<li>자기 어그리게이트(별점)를 어떻게 정리할지 스스로 결정할 수 있었습니다.</li>
</ul>
<p>아직 전체를 이벤트 기반으로 재구성한 단계는 아니지만,
“무슨 일이 일어났는가(What happened?)”를 기준으로 모듈을 연결하는 연습을 해볼 수 있었다는 점이 의미 있었습니다.</p>
<h4 id="좀-더-생각해본다면">좀 더 생각해본다면,</h4>
<ol>
<li><strong>이론적으로 더 느슨한 구조</strong></li>
</ol>
<ul>
<li>이벤트 자체에 삭제 가능 여부 플래그 등 <strong>처리에 필요한 정보들을 최대한 담아두고</strong>, <em>이벤트를 받은 쪽에서 다른 모듈을 다시 조회하지 않아도 되도록</em> 만드는 편이 더 느슨한 결합(Loosely Coupled)에 가깝습니다.</li>
</ul>
<ol start="2">
<li><strong>이번 구현에서의 타협</strong></li>
</ol>
<ul>
<li>이번 구현에서는 RoadMapQueryService 를 통해 한 번 더 로드맵을 조회한 뒤 삭제 가능 상태를 다시 확인하는 쪽을 선택했습니다.</li>
<li>운영/안정성 관점에서 “정말 삭제해도 되는 상태인지”를 한 번 더 방어적으로 확인한 셈이라, 완전히 잘못된 선택이라고 보긴 어렵지만, 의존성이 생긴다는 점은 인지하고 있습니다.</li>
</ul>
<ol start="3">
<li><strong>앞으로의 리팩터링 아이디어</strong></li>
</ol>
<ul>
<li>리팩터링한다면, <code>RoadMapDeleted</code> 나 <code>RoadMapVisibilityChanged</code> 처럼 <strong>의미가 더 분명한 이벤트로 분리하고</strong>, 그 안에 “삭제 가능한 상태인지”, “어떤 사유로 삭제/비공개가 되었는지” 등의 정보를 함께 담는 방향도 고려해 볼 수 있겠다고 느꼈습니다.</li>
<li>이렇게 되면 Star/Category 쪽은 RoadMap 을 다시 조회하지 않고도 “지금 이 이벤트를 어떻게 처리해야 하는지”를 더 자율적으로 판단할 수 있을 것 같습니다.</li>
</ul>
<blockquote>
<details>
<summary><b>👀 도메인 이벤트 흐름 보기</b></summary>

<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/9c37f53c-6a42-412f-bf55-cbb7505b5162/image.png" alt=""></p>
</details>
</blockquote>
<h2 id="2-dto-vs-projection-vs-read-model">2. DTO vs Projection vs Read Model</h2>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/63388da9-ff9d-4d03-b004-d3fb7a82c497/image.png" alt=""></p>
<p>이번 프로젝트를 하면서 DTO / Projection / Read Model의 역할을 한 번 정리하게 되었습니다.</p>
<p><strong><a href="https://dd-jiyun.notion.site/DTO-vs-Projection-vs-Read-Model-2b46e51932c7801c884ff3ea33c1a1b2?source=copy_link">📌 자세히 보기: DTO / Projection / Read Model 간단 정리</a></strong></p>
<p>조회 요구사항이 다양하다 보니,</p>
<ul>
<li>어디까지를 단순 DTO로 처리할지</li>
<li>어디서는 Projection으로 최적화할지</li>
<li>어디서는 아예 Read Model을 따로 빼는 게 좋을지</li>
</ul>
<p>를 고민하게 되었고, 그 과정에서 <strong>조회 전용 모델도 도메인 관점에서 설계할 수 있다</strong>는 관점을 조금이나마 갖게 되었습니다.</p>
<h1 id="아쉬웠던-점">아쉬웠던 점</h1>
<h2 id="1-기술적인-부분-category--star-도메인-관점">1) 기술적인 부분 (Category / Star 도메인 관점)</h2>
<ul>
<li>별점 확장 시나리오(Topic/SubTopic, 다른 도메인 대상 평가)를 먼저 상상해 보며 스키마를 더 과감하게 일반화해 보는 실험을 못 해본 점이 살짝 아쉽습니다.</li>
<li>테스트 코드에서 도메인 규칙과 시나리오를 더 분명히 드러냈다면, “이 도메인이 무엇을 보장해야 하는지”를 더 잘 설명할 수 있었을 것 같습니다.</li>
</ul>
<h2 id="2-협업적인-부분">2) 협업적인 부분</h2>
<ul>
<li><p>질문을 적극적으로 하지 못했던 부분 때문에, 
이해하지 못한 상태로 개발을 시작해 막막함을 느끼는 순간이 있었습니다.</p>
</li>
<li><p>정한 기간 안에 PR 리뷰와 머지를 지키지 못한 부분이 
팀 전체 흐름에 작게나마 영향을 주었다고 느꼈습니다.</p>
</li>
<li><p>테스트를 급하게 작성하면서 목데이터를 신경 쓰지 못한 부분은 
“테스트를 신뢰할 수 있는가?”라는 관점에서 아쉬움으로 남았습니다.</p>
</li>
<li><p>설계 단계에서 더 적극적으로 참여했다면, 
나중에 구현 단계에서 느꼈던 막막함을 줄일 수 있었을 것 같다는 생각도 들었습니다.</p>
</li>
</ul>
<h1 id="좋았던-점">좋았던 점</h1>
<ul>
<li><p>LXP라는 도메인 위에서 처음으로 DDD를 적극적으로 적용해 보면서,</p>
</li>
<li><p><em>도메인 중심으로 설계하고 코드를 바라보는 경험*</em>을 했습니다.</p>
</li>
<li><p>Notion, GitHub Discussion, 코드 리뷰를 통해
서로가 성장할 수 있도록 기록하고 리뷰하는 문화를 함께 만들어 갔다는 점이 좋았습니다.</p>
</li>
<li><p>바운디드 컨텍스트, 어그리게이트, 도메인 이벤트, 도메인 기반 패키지 구조, 
DTO/Projection/Read Model 등 책에서만 보던 개념들을 실제 코드에 녹여 보면서</p>
</li>
<li><p><em>“아 이래서 이런 개념이 필요하구나”*</em>를 몸으로 이해하기 시작했습니다.</p>
</li>
</ul>
<hr>
<h1 id="지난-목표-점검">지난 목표 점검</h1>
<p>이전 프로젝트 회고에서 다음과 같은 <a href="https://velog.io/@dd-jiyuni/wanted-project-01#%EB%8B%A4%EC%9D%8C-%EB%AA%A9%ED%91%9C">세 가지 목표</a>를 적어둔 적이 있습니다.</p>
<ol>
<li>코드 리뷰 시간 확보 및 규칙 정하기  </li>
<li>논의 주제에서의 본질을 벗어나지 않기 (실험: 논의 시간 타임박스)  </li>
<li>서로 성장할 수 있는 분위기를 만들기  </li>
</ol>
<p>이번 팀 프로젝트에서 이 세 가지 목표를 실제로 얼마나 실천해 봤는지 점검해보았습니다.</p>
<table>
<thead>
<tr>
<th><strong>목표</strong></th>
<th><strong>달성도</strong></th>
<th><strong>회고</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>코드 리뷰 문화</strong></td>
<td>✅</td>
<td>PR 단위 축소 및 리뷰 사이클 정착 성공. (규칙 구체화 필요)</td>
</tr>
<tr>
<td><strong>논의 본질 유지</strong></td>
<td>🟡</td>
<td>Notion 기록으로 논의 발산은 막았으나, 타임박싱은 미흡.</td>
</tr>
<tr>
<td><strong>성장 분위기</strong></td>
<td>✅</td>
<td>&quot;비난&quot;이 아닌 &quot;제안&quot;하는 리뷰 문화 정착.</td>
</tr>
</tbody></table>
<p>**<a href="https://dd-jiyun.notion.site/2b46e51932c780538297d3a9ba4fd136?source=copy_link">📌 자세히 보기: 지난 목표 점검</a>
**</p>
<h1 id="다음-목표">다음 목표</h1>
<p>이번 프로젝트를 계기로, 다음과 같은 목표를 갖게 되었습니다.</p>
<ul>
<li>어그리게이트 경계 잡는 연습 더 해보기</li>
<li>조회 모델 설계 연습 (DTO / Projection / Read Model)<ul>
<li>단순 DTO를 넘어서, “이 화면을 위한 Read Model은 무엇인가?”를 먼저 고민하고 구성해 보기.</li>
</ul>
</li>
<li>이해가 막히면 설계 단계에서 질문하기</li>
<li>작은 단위로 자주 PR 보내기</li>
<li>도메인 시나리오를 설명하는 테스트를 꾸준히 쌓아가기</li>
</ul>
<p>DDD를 한 번 써봤다고 해서, “이제 DDD를 안다”고 말할 수 있는 단계는 아니라고 생각합니다.</p>
<p>하지만 이번 경험을 통해, </p>
<blockquote>
<p>DDD는 정답이 정해진 규칙이 아니라, 복잡성과 싸우기 위한 하나의 사고방식이다.</p>
</blockquote>
<p>라는 문장에는 조금 더 자신 있게 동의하게 되었습니다.</p>
<p><em><strong>긴 글 읽어주셔서 감사합니다 🙌</strong></em></p>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://www.msaschool.io/operation/design/design-three/">이벤트 스토밍</a></li>
<li><a href="https://tech.kakaopay.com/post/backend-domain-driven-design/">카카오페이-여신코어 DDD</a></li>
<li><a href="https://www.youtube.com/watch?v=sLG5n_pXWK0">DDD 그렇게쓰는거 아닌데</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[LXP] 多事多難한 … 우리의 개발여정 ]]></title>
            <link>https://velog.io/@dd-jiyuni/wanted-project-01</link>
            <guid>https://velog.io/@dd-jiyuni/wanted-project-01</guid>
            <pubDate>Sun, 26 Oct 2025 12:21:37 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가기-전">들어가기 전</h1>
<p>LMS(Learning Management System)은 코로나 세대 대학생들은 많이 들어봤을텐데요!
저는 LXP를 처음 들었을 때 LMS랑 비슷한 친구구나 하고 넘어갔었어요.</p>
<p>하지만 프로젝트를 시작하기 전 어떤 차이가 있는지 알고 넘어가는게 좋겠다! 싶어서 들어가기 전에 간단히 소개를 해보려 합니다. </p>
<h2 id="lxp가-뭐죠">LXP가 뭐죠?</h2>
<p>LXP는 &quot;Learning eXperience Platform&quot;의 약자로 학습 경험 플랫폼을 의미해요.</p>
<p>LMS와 LXP는 &quot;온라인 학습을 위한 플랫폼&quot;이라는 공통점을 가져요.
하지만 둘은 초점이 달라요. LMS는 교수자(관리자) 중심의 교육 관리와 콘텐츠 제공에 초점을 두었다면, LXP는 학습자 중심의 개인화된 경험과 콘텐츠 추천에 초점을 두어요.</p>
<p>요즘은 점차 LMS -&gt; LXP의 형태의 플랫폼으로 변화하고 있다고 하네요 🙌</p>
<h1 id="본격-프로젝트-스타-또">본격 프로젝트 스타-또!</h1>
<blockquote>
<p><a href="https://github.com/TeamAnado/Project-LXP">👀 Anado 팀 _ LXP 프로젝트 Github 구경 가기</a></p>
</blockquote>
<h2 id="어쩌다가-팀장">어쩌다가 팀장</h2>
<p>프로젝트를 시작하기 전 별도의 스터디를 진행했으면 하는 강사님의 말씀에 총 5팀의 스터디 그룹이 생성됐어요! <del>(그 중 한 팀이 저희 Anado팀 입니당 ㅎㅎ)</del></p>
<p>저는 처음이라 나서지 말자.. 하는 마음에 자원하지 않았는데,, 저의 <del>초롱초롱 눈</del>이 강사님께 닿았는지 ㅎ 팀장 제의를 받게 되었습니다 !!</p>
<p>팀장으로 간택(?)이 되고 나서 어떻게 하면 모두가 좋은 학습을 할 수 있을지 많이 고민했던 것 같아요. 그래도 걱정했던 것과 다르게 다른 팀원 분들이 부담을 주지 않고 모두가 한 마음으로 으쌰으쌰하니까 시너지가 나더라구요 🥹</p>
<h2 id="나의-목표">나의 목표</h2>
<p>프로젝트를 진행하기 전 제가 얻어가고 싶은 부분이 무엇인지에 대해서 고민해봤어요.
자주 사용했던 Spring Boot를 사용하지 않고 <strong>Java + HikariCP</strong> 를 사용해서 구현을 했어요.</p>
<p>항상 JPA를 사용했던 터라 내부적으로 어떻게 동작을 하고, HikariCP를 사용할 때는 어떤 차이가 있는지에 대해서 잘 몰랐어요. </p>
<p>그래서 이번 프로젝트의 목표에서 학습과 협업을 가장 큰 키워드로 잡아 봤어요.</p>
<ul>
<li><strong>학습</strong> : 무심결에 사용했던 프로젝트 구조와 JPA가 아닌 HikariCP는 어떤 매력이 있는지 알아보자!</li>
<li><strong>협업</strong> : 나와는 다른 견해를 가진 팀원들과 논의를 자주 하며 의견을 전달하는 연습을 하자!</li>
</ul>
<p>위와 같이 조금은 세부적?인 목표를 가지고 시작했어요</p>
<h2 id="잘한-것-같아요">잘한 것 같아요!</h2>
<h3 id="중간-논의">중간 논의</h3>
<p>프로젝트를 시작하기 전 <a href="https://docs.google.com/spreadsheets/d/1PZwRmWYTLzCW2ySKdsum6PjQIfbJcz_QPZQF5ZnPwhA/edit?usp=sharing">요구사항 명세서 작성</a> 및 ERD 설계를 꼼꼼히 진행했어요.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/aa55c1d9-3679-4392-9193-9c6c0f231f24/image.png" alt=""></p>
<p>각자 도메인을 정하고 난 후 요구사항 명세를 진행하는데 서로 놓친 요구사항이 있다면 알려주는 과정이 인상적이었어요.</p>
<p>꼼꼼히 명세를 작성하고 도메인 분석 후 ERD 설계까지 이틀 정도의 시간동안 충분히 논의를 진행하여 최종적으로 위와 같은 결과가 나오게 되었어요. </p>
<h3 id="문서화">문서화</h3>
<p>중간 중간 논의하는 과정을 빠짐없이 기록하려 했어요.
각자 프로젝트 구현 시작 전 Daily Scrum을 작성하며 마지막에는 목표량을 달성했는지 확인도 하며 &quot;실무에서 이런 식으로 진행하겠구나!&quot;를 느낄 수 있었어요.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/8749a2d0-8bb3-4d2e-ab6a-807e38d3e82a/image.png" alt=""></p>
<h3 id="한-팀--한-마음">한 팀 = 한 마음</h3>
<p>논의 과정이나 프로젝트 구현 과정에서 아무도 빠짐없이 적극적으로 자신의 의견도 내고, 
구현하는 과정에서 궁금한 점이 생기면 가감없이 질문을 했어요.</p>
<p>사실 프로젝트를 위해 만들어진 팀이었다면 이러한 분위기가 나오지 않았을 것 같은데 사전에 스터디를 진행하며 친해졌던 게 큰 작용을 한 것 같아요.</p>
<p>그래서 각자 맡은 도메인 부분을 구현하기 바빴을 수 있었지만 모두가 자신의 일처럼 의견을 공유해주면서 더 빠른 시간 안에 해결할 수 있었던 것 같아요</p>
<p><strong>함께 해준 모든 팀원들에게 감사했던 시간이었습니다 🤭</strong></p>
<h2 id="아쉬웠어요">아쉬웠어요!</h2>
<p>좋은 점이 많았던 만큼 아쉬웠던 부분도 있었어요.</p>
<h3 id="현실적인-구현-목표">현실적인 구현 목표</h3>
<p>처음 저희는 요구사항과 ERD 설계에 시간을 다른 팀보다 많이 쓴 것 같다고 생각했어요.
물론 후회되는 건 아닙니다!</p>
<p>저희의 처음 도메인 분석 후 ERD는 아래와 같았어요. 
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/aa83b6d7-7569-4287-9e47-a958322116b8/image.png" alt=""></p>
<p>이렇게 문의, 리뷰도 있으면 좋겠다고 생각했죠.
하지만 주어진 시간은 제한적이고 핵심 기능 구현에 힘쓸 시간보다 다른 기능을 구현하는데에 힘을 쓸 것 같다는 의견이 구현하던 중간에 나오게 되었어요.</p>
<p>그래서 &quot;꼭 있어야 하는 기능&quot;을 추린 후의 최종적으로 <a href="https://velog.io/@dd-jiyuni/wanted-project-01#%EC%9E%98%ED%95%9C-%EA%B2%83-%EA%B0%99%EC%95%84%EC%9A%94">위의</a> ERD가 탄생하게 되었어요!</p>
<p>조금 더 현실적인 시간을 고려하고 핵심 위주의 설계에 초점을 두고, 이후에 부가 기능으로 구현했다면 좋았을 것 같다고 생각이 들었어요 😂</p>
<h3 id="끝없는-논의">끝없는 논의</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/40244a24-f03e-4b6e-8ea2-fc96284e716f/image.png" alt=""></p>
<p>분명 꼼꼼하게 정의했다고 생각했지만 가장 중요한 &quot;컨벤션&quot;에 대한 논의를 사전에 하지 못했어요. 그러다보니 중간에 위의 이미지와 같이 끝없는 논의의 시간이 있었어요. </p>
<p>한 주제로 논의를 진행하기 시작하면 모두 구현을 하다가 멈추게 되었어요.
논의가 끝나면 그에 관련된 꼬리 논의 주제가 나오고 그러다보니 생각했던 시간보다 많은 시간을 논의 시간에 쏟게 된 것 같아요. </p>
<p>이 과정을 통해서 논의 중에도 본질을 벗어나는 주제에 대해서는 멈출 줄도 알아야 한다는 것을 깨닫게 되었어요.</p>
<h3 id="코드-리뷰">코드 리뷰</h3>
<p>저희는 처음에 코드 리뷰의 문화를 경험해보자는 이야기가 나왔어요.
브랜치 전략을 <code>Main - Develop - Feature/{기능}</code>으로 가져가며 Develop -&gt; Main에 PR 규칙을 리뷰 2명으로 두었어요. </p>
<p>하지만, 각 도메인을 구현하느라 리뷰를 해줄 시간을 확보하지 못했어요.
그래서 <code>codeRabbit</code>이라는 리뷰 AI를 이용하며 자동화를 했어요. 코드 래빗이 너무 리뷰를 잘해주더라구요! </p>
<p>리뷰를 받는 것은 좋았지만 코드래빗에만 의존하며 리팩토링을 하게 되는 과정에서 각 팀원들이 서로의 코드를 이해하기 위한 과정이 생략된 것 같아 아쉬움이 남았어요.</p>
<h2 id="마치며">마치며</h2>
<h3 id="피드백--느낀점">피드백 &amp; 느낀점</h3>
<p>저희 포텐업의 프론트엔드와 백엔드 모두가 모여서 발표하는 시간을 가졌어요.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/6b7897b9-e7bd-4d8d-a2bc-671231c30a35/image.png" alt=""></p>
<p><del>(제가 발표하게 되어 얼마나 떨렸는지 몰라요!!)</del>
발표를 직접 보시고 강사님께 아래와 같은 피드백을 받았어요! </p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/f5100f08-ff39-4ec5-bebe-b429a552f520/image.png" alt=""></p>
<p>피드백을 보고 &#39;아쉬움을 느꼈던 부분도 발표할 때 비춰졌구나&#39;하고 느꼈어요.
더더욱 신경써야할 부분이라고 생각하게 되었습니다.</p>
<p>그래도 팀 분위기에 대해서 칭찬 받아서 기분 좋았습니다!</p>
<p>추가로 기술적 목표였던 HikariCP 사용 경험도 짧게 공유하자면, JPA의 &#39;편리함&#39; 뒤에 숨겨진 동작 원리를 고민해볼 수 있는 좋은 기회였어요.</p>
<p>직접 쿼리를 작성하고 Connection Pool을 관리하며 SQL의 중요성을 다시 한번 깨닫게 되었습니다. 물론, 반복적인 CRUD 작업을 처리할 때는 JPA가 그립더라구요 😆</p>
<h3 id="다음-목표">다음 목표</h3>
<ul>
<li>코드 리뷰 시간 확보 및 규칙 정하기!</li>
<li>논의 주제에서의 <strong>본질</strong>을 벗어나지 않기 -&gt; (실험) 논의 시간 타임박스 정해서 진행해보기!</li>
<li>서로 성장할 수 있는 분위기를 만들기!</li>
</ul>
<p>긴 글 읽어주셔서 감사합니다 🙇🏻‍♀️</p>
<hr>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://brunch.co.kr/@ubob/101">LXP vs. LMS</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JVM 내부에서는 어떤 일이 일어날까?]]></title>
            <link>https://velog.io/@dd-jiyuni/JVM-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%9D%BC%EC%9D%B4-%EC%9D%BC%EC%96%B4%EB%82%A0%EA%B9%8C</link>
            <guid>https://velog.io/@dd-jiyuni/JVM-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%9D%BC%EC%9D%B4-%EC%9D%BC%EC%96%B4%EB%82%A0%EA%B9%8C</guid>
            <pubDate>Fri, 10 Oct 2025 11:07:29 GMT</pubDate>
            <description><![CDATA[<p>어떤 프로그램을 다운로드할 때 &quot;Windows용&quot;, &quot;Mac용&quot;을 따로 받아야 했던 경험, 한 번쯤은 있으실 것입니다. 컴퓨터 세상에서는 이게 당연한 규칙이었습니다.</p>
<p>그런데 Java는 조금 특별합니다.</p>
<blockquote>
<p><strong>한 번 작성하면, 어디서든 실행된다 (Write Once, Run Anywhere)</strong></p>
</blockquote>
<p>개발자가 코드를 딱 한 번만 작성하면, 그 코드가 어떤 컴퓨터에서든 마법처럼 딱 맞게 돌아갑니다. 어떻게 가능할까요? 바로! JVM(Java Virtual Machine) 덕분입니다.</p>
<p>이번 편에서는 <code>java</code> 명령어 뒤에 숨겨진 JVM의 시동 과정을 함께 뜯어보려 합니다.</p>
<h2 id="우리가-java-코드를-작성하면">우리가 Java 코드를 작성하면</h2>
<p>우리가 작성하는 .java 파일은 어떻게 JVM에 전달될까요?</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/99dca840-47d9-44ba-989b-54c796b06fca/image.png" alt=""></p>
<ol>
<li>코드를 작성하면 javac 컴파일러가 Bytecode로 변환합니다.</li>
<li>변환된 .class file을 만들고,</li>
<li>각 OS에 설치된 JVM이 이 .class 파일을 읽어(로딩)들입니다.</li>
<li>해당 운영체제가 이해할 수 있는 기계어(Machine Code)로 번역하며 실행합니다.</li>
</ol>
<p>다음으로는 .class 파일이 JVM으로 전달된 후의 내부 동작에 대해서 알아보겠습니다.</p>
<h2 id="jvm-내부-동작---실행-3인방">JVM 내부 동작 - 실행 3인방</h2>
<p>JVM은 아래와 같은 내부 구조를 가지고 있습니다.</p>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/35fec058-b8a9-4b80-98b5-8d18a4af2a42/image.png" alt="JVM 내부 구조">

<h3 id="1-class-loader-클래스-로더">1. Class Loader (클래스 로더)</h3>
<p>클래스 파일을 찾아 그 내용(바이트코드)을 JVM의 메모리(Runtime Data Area)로 가져옵니다. 가져온 코드가 유효하고 안전한지 확인하는 과정이 있습니다. 이 과정이 없으면 어떤 코드도 실행될 수 없고 잠재적 보안 위험에 노출될 수 있습니다.</p>
<h4 id="역할">역할</h4>
<ul>
<li><p><strong>로딩(Loading)</strong>: <code>.class</code> 파일에서 바이트코드를 읽어와 JVM의 메소드 영역에 클래스 정보를 저장합니다.</p>
</li>
<li><p><strong>연결(Linking)</strong></p>
<ul>
<li>검증(Verification): 로드된 바이트코드가 Java 언어 사양과 JVM 규약을 준수하는지 확인하여 악성 코드나 잘못된 코드가 실행되는 것을 방지합니다.</li>
<li>준비(Preparation): 클래스의 static 필드(변수)에 필요한 메모리를 할당하고 기본값으로 초기화합니다.</li>
<li>해결(Resolution): 심볼릭 참조(예: System.out.println)를 실제 메모리 주소로 변환합니다.</li>
</ul>
</li>
</ul>
<ul>
<li><strong>초기화(Initialization)</strong>: static 필드에 개발자가 명시한 실제 값을 할당하고, static 블록을 실행합니다.</li>
</ul>
<h3 id="2-runtime-data-area-런타입-데이터-공간">2. Runtime Data Area (런타입 데이터 공간)</h3>
<p>JVM이 프로그램을 실행하는 과정에서 발생하는 모든 종류의 데이터(클래스 정보, 변수, 객체 등)를 효율적인 데이터 저장 및 접근, 그리고 생명 주기 관리를 위해 저장하고 관리할 체계적인 메모리 공간이 필요합니다. </p>
<p>나누어진 메모리 영역을 간단히 설명하면, </p>
<ul>
<li><p><strong>메소드 영역 (Method Area)</strong> : 클래스 정보, static 변수, 메소드 코드 등이 저장됩니다. JVM 프로세스 시작 시에 생성되고 종료 시까지 유지됩니다. 이 영역은 모든 스레드가 공유합니다.</p>
</li>
<li><p><strong>힙 영역 (Heap Area)</strong> : <code>new</code> 연산자로 생송된 객체(인스턴스)와 배열이 저장됩니다. 모든 스레드가 공유하고, 가비지 컬렉터(Garbage Collector; GC)의 주요 관리 대상입니다.</p>
</li>
<li><p><strong>스택 영역 (Stack Area)</strong> : 각 스레드마다 별도로 생성되며, 메소드 호출 정보(스택 프레임), 지역 변수, 매개변수 등이 임시적으로 저장됩니다. 
메소드가 호출되면 프레임이 푸시되고, 종료되면 팝(pop)되어 사라집니다.</p>
</li>
</ul>
<h3 id="3-execution-engine-실행-엔진">3. Execution Engine (실행 엔진)</h3>
<p>클래스 로더가 바이트코드를 메모리에 적재하고 런타임 데이터 영역에 데이터가 준비되어도, 실제로 그 바이트코드를 CPU가 이해할 수 있는 기계어로 변환하고 명령을 실행하는 주체가 없다면 프로그램은 동작할 수 없습니다.</p>
<p>실행 엔진이 바이트코드를 한 줄 한 줄 읽어내려가며 컴퓨터가 이해할 수 있는 기계어로 번역하고 실행합니다. 이때 주로 두 가지 방식을 사용합니다.</p>
<h4 id="1-인터프리터interpreter-방식">1. 인터프리터(Interpreter) 방식</h4>
<p>바이트코드를 한 명령어씩 읽어 즉시 기계어로 변환하고 실행합니다.</p>
<ul>
<li>장점: 컴파일 과정 없이 바로 실행할 수 있어 프로그램 시작 시간이 빠릅니다.</li>
<li>단점: 동일한 코드가 반복될 때마다 매번 해석해야 하므로 비효율적일 수 있습니다.</li>
</ul>
<h4 id="2-jit-컴파일러-just-in-time-compiler-방식">2. JIT 컴파일러 (Just-In-Time Compiler) 방식</h4>
<p>인터프리터 방식의 비효율을 해결하기 위한 방식입니다.</p>
<p>자주 실행되는 코드(Hotspot)를 <strong>프로파일링(Profiling)</strong> 하여 식별합니다.
이 식별된 바이트코드 블록을 런타임에 <strong>네이티브 코드(기계어)</strong> 로 컴파일하여 메모리에 저장합니다.
이후 동일한 코드가 호출될 때는 인터프리터를 거치지 않고 미리 컴파일된 네이티브 코드를 직접 실행하여 성능을 대폭 향상시킵니다.</p>
<h4 id="결론">결론</h4>
<p>실행 엔진은 처음 보는 코드는 인터프리터로 한 줄씩 처리하다가, 자주 쓰는 중요한 코드는 JIT 컴파일러로 통째로 최적화하는 두 가지 전략을 함께 사용합니다. </p>
<blockquote>
<p>Java는 처음에는 약간 느리게 시작하더라도, 실행될수록 점점 더 빨라지는 &#39;동적 최적화&#39;의 특징을 가지게 됩니다.</p>
</blockquote>
<h2 id="마무리">마무리</h2>
<p>지금까지 뒤에서 일어나는 JVM의 내부 동작 과정을 살펴봤습니다.</p>
<p>다음 편에서는 <strong>&#39;런타임 데이터 영역(JVM 메모리)&#39;</strong> 의 스택과 힙 메모리에 대해서 알아보겠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2025 상반기, 이렇게 살았습니다 😂]]></title>
            <link>https://velog.io/@dd-jiyuni/2025%EB%85%84-%EC%83%81%EB%B0%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/2025%EB%85%84-%EC%83%81%EB%B0%98%EA%B8%B0-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 21 Sep 2025 14:47:28 GMT</pubDate>
            <description><![CDATA[<p>벌써 2024년 회고를 쓰고 2025년의 반이 지났다.
다시 제대로 된 방향을 잡고 일어서기 위해 회고를 작성하려 한다.</p>
<h2 id="다시-학생-하고-싶어요">다시 학생 하고 싶어요</h2>
<p>올해 2월에 졸업을 했다. 정말 <strong>&#39;취업 준비생&#39;</strong> 이라는 타이틀이 생겼고 이에 대한 부담도 많아졌다.
어느 순간부터 조급함, 불안함이 밀려오고 다른 사람들과 비교하고 있는 나를 발견했다.
그러다보니 원래 동기가 되었던 것들이 다 무너지고 한없이 작아지고 있었다.</p>
<p>주변에 도움을 주려고 하는 사람들이 정말 많았지만, 도움을 받는 것에 대해서 기대에 부응해야 한다는 생각이 들었다.
이렇게 자존감이 많이 떨어진 상태이다보니 눈치를 많이 보게 되었던 것 같다.</p>
<p>모든 취업 준비를 하는 사람들이 다 겪을 수 있는 일이라고 생각했고, 이 모든 과정도 성장하는 중이라고 스스로 위로했다.
유독 잠들기 전 생각을 많이 하는 편이라 잠도 많이 설쳤다. </p>
<p>물론 이처럼 계속 우울한 생각만 한 것도 아니다! 이제 우울한 이야기는 끝내고 상반기에 어떤 활동을 했는지 적어보려한다. </p>
<h2 id="낯선-자리에서-얻은-에너지">낯선 자리에서 얻은 에너지</h2>
<p>학교를 졸업하니 항상 만나던 사람들만 만나고 새로운 사람들을 접할 기회가 많지 않았다.
그래서 다양한 사람들을 통해 에너지를 얻는 나에게 네트워킹은 좋은 자리였다.</p>
<h3 id="코스포korea-startup-forum---startup-24-7">코스포(Korea Startup Forum) - STARTUP 24-7</h3>
<p>스타트업 종사하는 현직 개발자 분들과 대화를 나누며 요즘 고민이나 어떤 트렌드인지 많이 배울 수 있었다. 
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/bc97aca0-72f1-4325-8680-c5cb38c3d0dd/image.JPG" width="400"/></p>
<h3 id="google-developer-groups-startup-lab-행사">Google Developer Groups Startup Lab 행사</h3>
<p>팀원이 구성되고 Gemini의 페르소나를 인터뷰하며 문제 인식부터 아이디어를 도출해가는 과정이 재미있었다.
좋은 분들을 많이 알게 되었다 !
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/36243e4d-0eb6-43cb-af4d-c77ed6fe9f50/image.png" width="600"></p>
<p>위의 네트워킹 뿐만 아니라 여러 네트워킹을 참여하며 시야를 넓힐 수 있었던 것 같다. </p>
<h2 id="끝까지-함께-가는-게-어렵더라">끝까지 함께 가는 게 어렵더라</h2>
<p>취업 준비를 하면서 당연히 사이드 프로젝트를 진행해야할 것 같다고 생각하여 다양한 플랫폼을 통해 좋은 아이디어를 찾아나섰다.
하지만, 교내에서 팀을 구성할 때와 달리 낯선 사람들과 한 마음으로 프로젝트 하나를 완성하는 것은 어려운 일이었다. 
어떤 것이 원인이 되었을 지 생각해보니 아이디어를 모으는 과정부터 기획자와 개발자 간의 원활하지 못한 소통이 문제였지 않을까 생각한다.
그래서 상반기 동안 제대로 된 팀 프로젝트 하나를 완성하지 못한 것이 너무 아쉽다.</p>
<p>그래도 꾸준히 해온 알고리즘 풀이를 다른 사람들과 스터디를 하면 재미있을 것 같다고 생각해서 사람들을 모집했다. 
스터디는 각 주차 별로 문제 풀이를 하고, 매주 모여 발표 및 궁금한 점을 질문하고 회고를 진행하였다. </p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/1efeb3e8-afcb-499f-8ddf-a468bbcc4770/image.png" alt=""></p>
<p>꾸준히 참여해준 팀원들에게 다시 한 번 감사함을 전하고 싶다. <del>(생각보다 뭐 하나 끝내는게 어렵더라구요 😂)</del></p>
<p>그리고 Java와 Spring 공부를 하고 있었기 때문에 좋은 기회로 &quot;초록해듀오&quot; 스터디를 참여하게 되었다.
우아한테크코스를 수료한 분들에게 약 3개월 정도 직접 멘토링을 받을 수 있는 좋은 프로그램이다.
리뷰하는 방법, 문제을 위한 사고 방식 등을 정말 많이 배울 수 있었다. <del>(거의 우테코 찬양 🫶🏻)</del></p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/c24164da-e377-4a28-899e-66e0fd6f7e79/image.png" alt=""></p>
<h2 id="오랜만에-여유">오랜만에 여유</h2>
<p>취업 준비를 하면서 가장 좋았던 건 시간을 맞춰 바빠서 못만났던 사람들과 만날 수 있었던 것이다.
가족들과도 친구들과도 좋은 시간을 보낼 수 있었다. </p>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/d107b512-81cd-42b1-a260-ec79baeb1cd0/image.JPG" width="600">
맨날 시험 기간 겹쳐서 못봤던 벚꽃도 실~~컷 봤다! 🌸

<h2 id="혼자서는-못하겠더라">혼자서는 못하겠더라</h2>
<p>6개월 정도 혼자 취업 준비를 하다보니 규칙적인 루틴과 강제성이 나에게 필요하다는 것을 깨달았다.
그래서 아래와 같은 기준으로 부트캠프를 선정했다.</p>
<ul>
<li>오프라인으로 수업이 진행되는가?</li>
<li>프론트엔드와의 협업이 이루어지는가?</li>
<li>커리큘럼이 따라가기 어렵게 이루어져있지 않은가? (ex. MSA, Kafka..)</li>
</ul>
<p>위의 기준에 충족하는 부트캠프를 찾다가 <strong>&#39;원티드 포텐업&#39;</strong> 이라는 부트캠프를 알게되었고, 이전 원티드 게더링에서 상담을 진행하는 것도 구경한 적이 있었다.</p>
<p>1기라는 키워드에 살짝 걱정이 되긴했지만, 붙으면 생각하자는 마인드로 지원하고 면접까지 진행하였다.</p>
<p>걱정과 달리 질문에 대해서 디테일하게 답변해주시고 운영해주시는 분들이 얼마나 많이 신경을 썼는지 알 수 있어서 걱정이 사라졌다. </p>
<p>신중하게 결정한 만큼 어떤 것을 위해 부트캠프에 왔는지를 더 명확하게 정의할 필요가 있다고 생각하였다. </p>
<h2 id="더-단단해지기-위해">더 단단해지기 위해</h2>
<ul>
<li><p>프로젝트 1개 완성
단순한 완성이 끝이 아닌 운영할 수 있는 서비스를 하나 만들고 싶다. </p>
</li>
<li><p>완벽주의 깨부시기
글 하나를 작성하더라도, 공부한 내용을 기록하더라도 항상 완벽하고자 하는 욕심때문에 시작도 못한 적이 많다. 
이번 부트캠프를 하면서 글을 많이 쓸 것이다.
마감 기한을 정해놓고 그 안에 글을 쓰는 연습을 하니 좀 도움이 되는 것 같다.</p>
</li>
<li><p>자신감 높이기
무작정 자신감만 높이는 것이 아닌 스스로의 실력에 대한 믿음, 기준을 잘 다져서 면접에서 자신감있는 모습을 보여주고 싶다.</p>
</li>
<li><p>지원하기
&#39;아직 준비가 덜 됐어..&#39;라고 생각해서 적극적으로 지원하지 않았는데 부트캠프를 하면서도 지속적으로 지원을 해보려고 한다. </p>
</li>
</ul>
<p>이제 일주일 됐지만, 앞으로 성장하고 바뀌는 모습을 꾸준히 작성해보려 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[왜 Github Flow를 선택했을까?]]></title>
            <link>https://velog.io/@dd-jiyuni/%EC%99%9C-Github-Flow%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%96%88%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@dd-jiyuni/%EC%99%9C-Github-Flow%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%96%88%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Thu, 18 Sep 2025 13:27:20 GMT</pubDate>
            <description><![CDATA[<p>항상 습관적으로 브랜치 구조를 <code>main - develop - feature/~~</code> 로 써왔는데,
이번에 수업을 듣고 git에도 다양한 플로우가 있다는 걸 알게되었어요. </p>
<h2 id="git에도-flow가-있어요">Git에도 Flow가 있어요.</h2>
<p>가장 많이 쓰이는 플로우를 위주로 알아볼게요.</p>
<h3 id="git-flow">Git Flow</h3>
<p>깐깐하고 체계적인 브랜치 관리가 필요할 때 사용하는 전략이에요.
버전별로 명확한 릴리스 계획이 있는 프로젝트에 가장 적합한 플로우에요.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/2097f522-b248-420f-91f2-85458c620d26/image.png" alt=""></p>
<blockquote>
<p>출처 : <a href="https://techblog.woowahan.com/2553/">https://techblog.woowahan.com/2553/</a></p>
</blockquote>
<p>각 브랜치에 대해서 알아보면,</p>
<ul>
<li><code>main</code>: 제품 출시 버전을 관리하는 메인 브랜치</li>
<li><code>develop</code>: 다음 출시 버전을 위해 개발하는 브랜치</li>
<li><code>feature</code>: 새로운 기능을 개발하는 브랜치</li>
<li><code>release</code>: 다음 출시 버전을 준비하는 브랜치</li>
<li><code>hotfix</code>: 출시된 제품의 버그를 고치기 위한 브랜치</li>
</ul>
<h3 id="github-flow">Github Flow</h3>
<p>Git Flow와 비교하면 복잡함을 줄이고 <strong>단순함과 속도</strong>에 초점을 맞춘 전략이에요. 
그래서 수시로 배포가 일어나는 웹 애플리케이션에 제일 적합해요. <del>(위에서 앞서 말했던 습관이 Github Flow였나봐요)</del></p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/dcccc8b7-8dfc-4d0e-9c4d-5661a69d3f9a/image.png" alt=""></p>
<p>Git 플로우 전략과 다르게 Github 플로우는 아래 두 가지 브랜치를 이용해요.</p>
<ul>
<li><code>main</code> : 배포를 위한 소스코드를 관리하는 브랜치</li>
<li><code>feature</code> : 새로운 기능 개발 브랜치</li>
</ul>
<h2 id="저희-anado에서는">저희 &#39;Anado&#39;에서는</h2>
<p>스터디 목표는 <strong>기술 블로그 작성을 통해 지식을 정제하여 구성원들의 역량을 끌어올리는</strong> 거에요.
그래서 작성된 아티클을 빠르게 공유하고 손쉽게 교정하기 위해 <strong>Github Flow 전략</strong>이 가장 적당하다고 판단했어요.</p>
<p>아래는 앞으로 진행될 스터디 github에요! 지켜봐주세요 😆</p>
<blockquote>
<p><a href="https://github.com/TeamAnado/AnadoBlog">https://github.com/TeamAnado/AnadoBlog</a></p>
</blockquote>
<h2 id="결론">결론</h2>
<p>앞으로 아래 기준으로 플로우를 선택하려고 해요 !</p>
<blockquote>
<p>팀의 배포 주기가 길고 버전 관리가 필요하다면 ? Git Flow
짧고 빠른 배포가 필요하다면 ? Github Flow</p>
</blockquote>
<hr>
<h3 id="참고자료">참고자료</h3>
<ul>
<li><a href="https://techblog.woowahan.com/2553/">우린 Git-flow를 사용하고 있어요 - 우아한 기술 블로그</a></li>
<li><a href="https://devocean.sk.com/blog/techBoardDetail.do?ID=165571&amp;boardType=techBlog">Git Branch 전략 비교 - Devocean</a></li>
<li><a href="https://inpa.tistory.com/entry/GIT-%E2%9A%A1%EF%B8%8F-github-flow-git-flow-%F0%9F%93%88-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%84%EB%9E%B5#git-flow_%EB%B8%8C%EB%9E%9C%EC%B9%98_%EA%B5%AC%EC%A1%B0">깃 브랜치 전략 정리 - Inpa_Dev</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[우당탕탕 '2024' 돌아보기]]></title>
            <link>https://velog.io/@dd-jiyuni/2024-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/2024-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Thu, 13 Mar 2025 09:10:46 GMT</pubDate>
            <description><![CDATA[<p>너무너무 늦어버린 2024년의 회고를 써보려고 한다.
2024년은 참 롤러코스터 같은 한 해였다. 
많은 일들이 있었지만 큰 의미를 주었던 것들을 위주로 작성해 볼 것이다! 회고 열차 출발합니다 💨💨</p>
<h2 id="걸어다니는-알바몬-탈출">걸어다니는 알바몬 탈출!</h2>
<p>때는.. 바야흐로 고등학생 시절, 갑자기 사회 경험을 해보고 싶어 뽑아주는 곳도 많지 않았지만 어찌저찌 아르바이트를 시작하게 되었다. 일하면서 다양한 사람들을 만나게 되었고 이 시절 나는 &#39;재미있고 돈도 벌고 일석이조네!&#39;라고 생각했다. 그러다 보니 2023년까지 다양한 종류의 아르바이트를 하게 되었고, 쉬지 않았다. </p>
<p>하지만 학교를 본격적으로 다니기 시작하면서 &#39;내가 뭘 위해 아르바이트에 몰두하고 있지? 공부를 이렇게 해 본 적이 있나?&#39;하는 생각이 들었다. 사실 아르바이트를 하지 않는 &#39;나&#39;가 너무 어색했다. </p>
<p><strong>그래서 2024년은 아르바이트를 안 하고 공부에 매진해 보자고 다짐했다!</strong></p>
<p>아르바이트를 한 것이 후회되냐고 묻는다면 난 한 3초 망설이다가 아니라고 대답할 것 같다.
처음에는 아르바이트를 할 시간에 공부를 더 했으면.. 하는 후회도 했지만 당시 내가 한 선택이었으며 손님 응대, 빠른 환경 적응, 진상 퇴치 등의 기술력을 습득할 수 있었기에 땅을 치고 후회하지는 않는다 😂</p>
<h2 id="우아한테크코스-도전">&#39;우아한테크코스&#39; 도전!</h2>
<h3 id="부제--모르는-건-부끄러운-게-아니다">부제 : 모르는 건 부끄러운 게 아니다.</h3>
<p>나는 &#39;6기 우아한테크코스&#39;에 도전했었다. 그때보다 이번에 지원할 때는 조금 더 진지하게 임했다. 그렇다고 6기 때 대충 했다는 것은 아니다!!! 절대!!</p>
<p>우아한테크코스를 지원하면 프리코스를 진행하는데 정말 많이 배운다 👍</p>
<p>이전 프리코스를 진행할 때는 문제를 이해하는 데에도 급급하고, 사람들과 공유하며 성장한다는 건 생각도 못 했고 미션을 해결하는 데에만 신경을 썼다. 살짝 해결보다는 해치우는 느낌이 강했던 것 같다. </p>
<p>하지만 이번 7기 때는 어떻게 더 나은 코드를 짤 수 있을지도 고민을 충분히 할 수 있었고, 매주 <a href="https://velog.io/@dd-jiyuni/posts?tag=%EC%9A%B0%EC%95%84%ED%95%9C-%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0">회고</a>도 진행하면서 다른 사람들과 코드 리뷰도 했다. </p>
<p>나는 누군가에게 못난 모습을 보여주는 것을 좋아하지 않아서 코딩할 때도 모르는 것을 들키지 않기 위해 아는 척했었다. 하지만 이 행동이 좋지 못한 것을 스스로 인지하고 있었다. </p>
<p>그래서 선배에게 조언을 구했다. 선배는 모르는 것을 질문할 줄 아는 것이 배울 기회가 생기는 것이고, 자신이 모른다는 걸 모르는 게 더 위험하다고 말했다. 
또한, 아는 척을 하다가 들켜버리면 신뢰가 떨어지는 건 당연하다고..😣</p>
<p>이 말을 듣고 머리를 쾅 맞은 거 같았다. 그렇다고 모르는 걸 알아보지도 않고 질문하는 건 나도 좋아하지 않기 때문에 *<em>질문을 잘하기 위해 *</em> 노력했다. 지금도 노력 중이다!</p>
<p>그래서 이번 프리코스에서 상호 리뷰를 하고, 함께 참여하는 동료들에게도 적극적으로 질문하면서 참여해서 더 많은 것을 얻어 갈 수 있었던 것 같다. 야호!!</p>
<h2 id="lg-u-유레카-도전">&#39;LG U+ 유레카&#39; 도전!</h2>
<p>부트캠프를 고를 때 나만의 기준이 있었다.</p>
<ol>
<li>오프라인 수업인지</li>
<li>커리큘럼이 괜찮은지</li>
<li>취업 지원을 잘해주는지 / 혜택이 있는지</li>
</ol>
<p>근데 딱 위의 조건에 맞는 유레카 부트캠프를 알게 되어 열심히 준비했다. 심지어 LG U+ 서류 면제 혜택까지 있었다. 절차는 서류 - 코테 - 면접 순이었다. </p>
<p>서류는 문항이 정해져 있었지만, 연습한다고 생각하고 한 100번은 수정한 것 같다. <del>(넘 과장했나..헤)</del></p>
<p>그리고 코테는 백준, 프로그래머스와 같이 알고리즘을 써서 푸는 문제가 아니었다. 두 문제였는데 단계가 4~5단계로 코드의 확장성을 생각하면서 풀어야 하는 문제들이었다.</p>
<p>운이 좋게 서류와 코테가 붙었다!!! </p>
<p>동아리 면접 외에 처음 보는 면접이라 어떻게 준비해야 할 지 감이 안 잡혔다.
내가 작성한 서류를 기반으로 예상 질문 추리고, 답변 준비하고 기본 질문 등 일반적인 면접 준비하듯 했다.</p>
<p>유튜브로 면접 관련 영상 다 본 것 같다. 눈 감으면 질문 생각 답변 복기하느라 잠도 제대로 못 잤다. 면접이란 이런걸까..?ㅠㅠㅠ 당일 아침까지 열심히 준비하고 면접을 봤다. </p>
<p>*<em>너무 서론이 길었는데 결론은, 떨어졌다 ! 와하하.. 면접 때 말을 잘 못했나 보다. 조금 더 논리적으로 얘기했어야 했는데.. *</em></p>
<p><strong>우울했지만, 좋은 경험이었다고 생각하고 다음엔 더 잘하면 된다!!!</strong></p>
<h2 id="좋사좋추-좋은-사람들과-좋은-추억이라는-뜻">좋사좋추 (좋은 사람들과 좋은 추억이라는 뜻..)</h2>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/920b958d-ccd4-4c18-ae15-c64d5f8b18b7/image.png" 
     style="display: block; margin: auto; width: 200px;"></p>
<p>2024년은 유독 좋은 사람들과 시간을 많이 보냈다.
가족들, 친구들이랑 여행도 다니고 대학 동기들이랑도 더 친해진 것 같다.</p>
<p>나는 내가 스스로 깊은 이야기를 잘 안하려고 한다고 생각했는데 누구랑 이야기를 하는지에 따라 달라지는 게 너무 신기했다. 진지한 대화를 할 수 있는 주변 사람들이 있다는 게 너무 소중하다.</p>
<p><strong>앞으로도 내 사람들을 잘 챙기면서 계속 좋은 추억을 쌓고 싶다 🤭</strong></p>
<h2 id="내가-벌써-졸업해도-되는-건가">내가 벌써 졸업해도 되는 건가?</h2>
<p>4학년을 마치고 2025년 2월에 졸업한다.
아직 마음만은 새내기인데 벌써 졸업한다는 게 실감이 안 난다.
이제 정말 취업 준비가 코 앞으로 다가왔다. 기대는 한 20프로.. 걱정이 80프로다 ㅎ..</p>
<p><strong>2025년은 조급해하지 않고 최선의 선택을 할 수 있는 사람이 될 것이다!</strong></p>
<h2 id="마무리">마무리</h2>
<p>회고를 어떻게 해야 잘 쓸 수 있을 지 아직은 잘 모르겠다.
주제 별로 느낀 점을 작성하면서 그래도 내가 성장하고 있다는 것을 느꼈다 🔥</p>
<hr>
<p>모두 2025년 건강하고 하고 싶은 일 모두 잘 되길 바랍니다!
긴 글 읽어주셔서 감사합니다 🙇🏻‍♀️</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 4주차 프리코스 회고]]></title>
            <link>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-4%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 18 Nov 2024 15:50:34 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전에">들어가기 전에</h2>
<p>4주 차 문제를 읽자마자 &quot;ㅇ..ㅔ..읭??!&quot;를 수십번은 한 것 같습니다.
이해했다고 생각했지만 볼 때마다 새로운 느낌..
이번 회고에서는 &quot;반성&quot;이 주가 될 것 같아요.</p>
<h2 id="무슨-문제가-있었냐면">무슨 문제가 있었냐면..</h2>
<h3 id="0-설계">0. 설계</h3>
<p>1주 차부터 3주 차 미션까지 진행하면서 설계에 많은 시간을 투자해서 구현을 급하게 하는 경우가 종종 있었습니다. 그리고 스스로 생각하기에 설계를 잘했다고 생각해도 구현하는 과정에서 수정되는 일이 빈번했습니다. 그래서 이번 4주 차 미션에는 설계를 빠르게 하고 구현하면서 설계를 수정하는 방법으로 진행할 생각이었습니다.
하지만, 문제를 읽으면서 &quot;빠르게 설계를 못하겠구나...&quot; 하고 생각했습니다.
이유는 문제도 제대로 이해하기 어려웠기 때문입니다. 이해한 게 맞는지에 대한 의심도 들고 놓친 부분도 많았습니다.</p>
<h3 id="1-프로모션-관련-메세지">1. 프로모션 관련 메세지</h3>
<blockquote>
<h4 id="프로모션-적용이-가능한-상품에-대해-고객이-해당-수량만큼-가져오지-않았을-경우-혜택에-대한-안내-메시지를-출력한다">프로모션 적용이 가능한 상품에 대해 고객이 해당 수량만큼 가져오지 않았을 경우, 혜택에 대한 안내 메시지를 출력한다.</h4>
</blockquote>
<ul>
<li>현재 {상품명}은(는) 1개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)<blockquote>
<h4 id="프로모션-재고가-부족하여-일부-수량을-프로모션-혜택-없이-결제해야-하는-경우-일부-수량에-대해-정가로-결제할지-여부에-대한-안내-메시지를-출력한다">프로모션 재고가 부족하여 일부 수량을 프로모션 혜택 없이 결제해야 하는 경우, 일부 수량에 대해 정가로 결제할지 여부에 대한 안내 메시지를 출력한다.</h4>
</blockquote>
</li>
<li>현재 {상품명} {수량} 개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)</li>
</ul>
<p>위와 같은 조건에 맞는 메세지를 출력합니다. 제가 헤맸던 부분은 예를 들어 사용자가 2+1 상품을 3개를 구매했을 경우는 메세지가 출력되지 않고, 2개를 구매했을 경우에만 1개를 증정받을 것이냐는 메세지를 출력하기 위해서 <code>(buy + get) / 사용자 구매량 * get</code> 이러한 계산식으로 메세지의 여부를 결정했는데 3개를 구매해도 1개 증정받을 것이냐는 메세지를 출력하게 되어 여러 조건문을 사용하게 되었습니다. 1+1과 2+1을 나누어 조건문을 작성하니 중복되는 조건문도 많고,, 정말 더러운 코드가 만들어졌습니다.</p>
<h3 id="2-증정-상품-관리">2. 증정 상품 관리</h3>
<p>구매한 상품은 따로 관리하니 영수증 출력 시 상품을 가져오는 것에는 문제가 없었습니다. 만약 사용자가 증정품을 추가하거나 이미 증정품의 수량까지 가져온 경우에는 증정품을 어떻게 관리해야 하는지 도무지 떠오르지 않았습니다. 그래서 증정품이 없는 영수증을 출력하게 되었습니다. 수량은 증가하고 프로모션 할인도 적용되지만 어떤 상품이 프로모션 증정 상품인지는 모르는 상태가 되어버렸습니다.</p>
<h3 id="3-미완성">3. 미완성</h3>
<p>위와 같은 문제들을 해결해야 다른 기능도 완성을 할 수 있다 보니 결국 완성을 하지 못했습니다 😭
시간은 없고 해결은 해야 하니 어느 순간 GPT랑 싸우고 있더라구요.. 지원서에 생성형 AI 사용하지 않고 스스로 힘으로 해내겠다고 한 목표를 지키지 못했습니다..
싸우면서 GPT도 한계가 있어 찾지 못하는 문제점을 디버깅을 통해서 찾는 과정에서 디버깅이 더 효율적이라는 것을 알게 되었습니다. </p>
<h2 id="원래-목표는요">원래 목표는요..</h2>
<ul>
<li>DTO 사용해 보기</li>
<li>도메인 스스로 검증</li>
<li>클래스 분리와 단위테스트</li>
<li>완성!!</li>
</ul>
<p>지금까지 미션에서 여러 리뷰를 받으면서 학습한 내용을 적용하는 것이 저의 가장 큰 목표였습니다. 물론, 기본적으로 단위테스트나 요구사항은 기본적인 목표였구요!</p>
<p>하지만 위의 나열한 목표 중에서 &quot;이건 진짜 완벽하다&quot;하고 이룬 것은 없는 것 같습니다. 설계하면서 목표를 지키려고 하니 더욱 복잡해졌습니다. 제가 너무 큰 욕심을 부린 것이 아닌가 싶습니다.</p>
<h2 id="진짜-마지막이네요">진짜 마지막이네요..</h2>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/6ee7fbbc-485d-4c7d-8bd4-970b865c1300/image.png" width="300" height="300">


<p>1주 차부터 4주 차까지 정말 열심히 달려온 것 같습니다.
많은 분과 소통하면서 서로 리뷰도 하면서 정말 많은 에너지와 정보(?)를 얻을 수 있어서 도전한 것을 너무 잘했다고 생각합니다.
비록 마지막 미션을 완성하지 못한 채 제출해서 아쉽지만 이번 프리코스를 통해서 학습하는 것에 대해서 흥미를 느낀 것 같습니다!</p>
<p>끝이 났어도 저는 프리코스 문제들을 꾸준히 리팩토링 해 볼 생각입니다! 지켜봐 주세요! </p>
<p>지금까지 저의 코드를 리뷰해 주신 분들, 많이 응원해 주신 분들 모두 잘 되셨으면 좋겠습니다! 이런 기회를 갖게 해준 프리코스도 너무너무 감사합니다 🙇🏻‍♀️</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 3주차 프리코스 회고]]></title>
            <link>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-3%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-3%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 06 Nov 2024 16:47:53 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전에">들어가기 전에!</h2>
<p>이번 미션은 로또를 자주 구매하는 편이 아니라서 문제를 잘못 이해한 점과 목표했던 것을 잘 이행했는지에 대한 의문과 마지막에 급한 수정으로 인한 깔끔하지 못한 코드 등.. 참.. 다사다난했던 것 같습니다.. 🥲</p>
<p>“오히려 회고 때 적을 게 많아졌네?! 완전 럭키Vㅣ키잖아! 🍀 ” 하고 마인드 컨트롤을 하고 회고를 작성해보겠습니다.</p>
<h2 id="이전-리뷰에서요">이전 리뷰에서요!</h2>
<p>이전 미션에서 불변 객체와 함수(메서드) 작명에 대한 피드백을 받았고, 각각의 개념을 구체적으로 이해하고 적용하기 위해 학습을 진행했습니다.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/f9563a7d-e83b-4e17-a529-b9dbda8acb04/image.png" alt=""></p>
<h3 id="불변-객체">불변 객체</h3>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/96bd302d-348b-4fd6-ac98-e83717821641/image.png" width="50%" />

<p>위의 개념이 적용된 불변 객체는 생성된 후에는 내부 상태가 절대 변경되지 않는 객체를 의미합니다. 즉, <strong>외부에서 수정할 수 없도록 제한하는 방식</strong>입니다.</p>
<p><strong>Java에서 불변 객체의 특징</strong>은,</p>
<ol>
<li>필드를 <code>final</code>로 선언하여 객체의 상태를 변경할 수 없도록 합니다.</li>
<li><code>Setter</code> 메서드를 제공하지 않아서 외부에서 값을 변경하지 못하도록 제한합니다.</li>
<li>내부에 가변 객체가 포함된 경우에는 참조를 직접 반환하지 않고 복사본을 반환하여 상태 변화가 일어나지 않도록 합니다. </li>
</ol>
<h3 id="함수메서드-작명">함수(메서드) 작명</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/71ec6d12-4084-4cc8-ab4b-3ad2dc382467/image.png" alt=""></p>
<p>메서드 작명은 항상 주의하려고 노력하지만, 가끔 메서드의 역할을 명확히 표현하지 못하거나 중복된 이름을 사용하는 경우가 생깁니다. 
이번 학습을 통해 메서드 작명에 대해 구체적인 기준을 세워봤습니다.</p>
<ul>
<li><strong>직관적이고 명확한 메서드명</strong>을 사용하여 메서드의 역할을 쉽게 파악할 수 있도록 합니다.</li>
<li>변수명에 자료형을 포함하지 않습니다. 예를 들어, inputList 대신 inputs처럼 <strong>변수의 역할에 집중한 이름</strong>을 사용합니다.</li>
<li><strong>중복되는 이름을 포함하지 않습니다.</strong> 예를 들어, <code>InputView.inputCount()</code>처럼 클래스명과 메서드명이 중복되는 경우, 메서드명을 간결하게 변경하여 중복을 피합니다.</li>
</ul>
<h2 id="로또">로또</h2>
<h3 id="기능-요구-사항"><strong>기능 요구 사항</strong></h3>
<p>간단한 로또 발매기를 구현한다.</p>
<ul>
<li>로또 번호의 숫자 범위는 1~45까지이다.</li>
<li>1개의 로또를 발행할 때 중복되지 않는 6개의 숫자를 뽑는다.</li>
<li>당첨 번호 추첨 시 중복되지 않는 숫자 6개와 보너스 번호 1개를 뽑는다.</li>
<li>당첨은 1등부터 5등까지 있다. 당첨 기준과 금액은 아래와 같다.<ul>
<li>1등: 6개 번호 일치 / 2,000,000,000원</li>
<li>2등: 5개 번호 + 보너스 번호 일치 / 30,000,000원</li>
<li>3등: 5개 번호 일치 / 1,500,000원</li>
<li>4등: 4개 번호 일치 / 50,000원</li>
<li>5등: 3개 번호 일치 / 5,000원</li>
</ul>
</li>
<li>로또 구입 금액을 입력하면 구입 금액에 해당하는 만큼 로또를 발행해야 한다.</li>
<li>로또 1장의 가격은 1,000원이다.</li>
<li>당첨 번호와 보너스 번호를 입력받는다.</li>
<li>사용자가 구매한 로또 번호와 당첨 번호를 비교하여 당첨 내역 및 수익률을 출력하고 로또 게임을 종료한다.</li>
<li>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>을 발생시키고, <code>[ERROR]</code>로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.<ul>
<li><code>Exception</code>이 아닌 <code>IllegalArgumentException</code>, <code>IllegalStateException</code> 등과 같은 명확한 유형을 처리한다.</li>
</ul>
</li>
</ul>
<h3 id="실행-결과">실행 결과</h3>
<pre><code class="language-prolog">구입금액을 입력해 주세요.
8000

8개를 구매했습니다.
[8, 21, 23, 41, 42, 43]
[3, 5, 11, 16, 32, 38]
[7, 11, 16, 35, 36, 44]
[1, 8, 11, 31, 41, 42]
[13, 14, 16, 38, 42, 45]
[7, 11, 30, 40, 42, 43]
[2, 13, 22, 32, 38, 45]
[1, 3, 5, 14, 22, 45]

당첨 번호를 입력해 주세요.
1,2,3,4,5,6

보너스 번호를 입력해 주세요.
7

당첨 통계
---
3개 일치 (5,000원) - 1개
4개 일치 (50,000원) - 0개
5개 일치 (1,500,000원) - 0개
5개 일치, 보너스 볼 일치 (30,000,000원) - 0개
6개 일치 (2,000,000,000원) - 0개
총 수익률은 62.5%입니다.</code></pre>
<h3 id="요구-사항-요약">요구 사항 요약</h3>
<ul>
<li>indent depth(들여쓰기)가 <strong>3이상</strong>이 되면 안된다.</li>
<li><strong>3항 연산자</strong>는 쓰면 안된다.</li>
<li>함수(메서드)가 <strong>한 가지 일만 하고 15 라인</strong>이 넘어가지 않아야 한다.</li>
<li><strong>else 예약어</strong>는 쓰지 않는다.</li>
<li><strong>Java Enum</strong>을 적용한다.</li>
<li><strong>단위 테스트*</strong>를 작성한다.</li>
</ul>
<h2 id="제-목표는요">제 목표는요!</h2>
<ul>
<li>요구 사항을 잘 지키자. (특히, 15 라인 넘기지 않는 것과 depth 3이상)</li>
<li>Enum을 써보자!</li>
<li>불변 객체를 적극적으로 사용해보자.</li>
<li>완성해보자!</li>
</ul>
<h2 id="이런-걸-고민했어요">이런 걸 고민했어요!</h2>
<h3 id="로또가어떻게-되더라">로또가..어떻게 되더라..?</h3>
<p>미션을 받으면 가장 먼저 설계를 위해 문제를 분석합니다.
평소 로또를 잘 구매하지 않았던 저는.. 로또의 발급 과정에 대해서 잘 몰랐습니다..
지금 생각해보면 &#39;왜 그렇게 생각했지?&#39; 하지만 다음에는 이런 실수를 하지 않기 위해 작성해봅니다. 이번엔 그림을 그려가면서 어떤 흐름을 가지고 있는지 어떤 협력이 필요할 지 고민했습니다.
<del>(부끄럽네요//)</del>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/7a5407ea-37b1-4dc4-9341-a116e0a2cc60/image.png" alt=""></p>
<p>저는 사용자에게 당첨 번호를 받는 것이 아닌 로또 번호를 받는 거라고 생각했습니다. 그래서 로또 번호 생성기가 당첨 번호까지 랜덤으로 생성되는 걸로 이해를..했습니다.
<del>(이미 로또가 개수만큼 발행되는데 ,, 왜그랬는지 참..)</del>
아무튼! 저 설계는 틀렸지만 흐름을 이해하는 데에는 도움이 됐습니다! </p>
<h3 id="검증은-누가-책임질래">검증은 누가 책임질래!?</h3>
<p>지금까지 <code>Validator</code>를 만들어서 한 클래스에서 관리하게 했습니다. 
그런데 주어진 <code>Lotto</code> 객체 안에 검증 메서드가 들어있는 것을 보고 이번에는 객체가 스스로 검증하는 방식으로 해야하나 하고 고민했습니다.
하지만 저는 이번에도 <code>Validator</code>를 만들었습니다. 이유는 공통적인 검증 과정이 객체마다 중복되면 오히려 관리하기 어려울 것이라고 판단했기 때문입니다.
미션이 끝난 후 많은 분들의 코드를 보니 객체가 스스로 검증하게 하는 코드를 많이 봤습니다. 이후에 생각해보니 공통 검증 과정을 클래스로 분리하고 각 객체별로 필요한 검증을 따로 두는 것이 더 좋았을 것 같다고 생각이 듭니다. 
검증에 대한 고민을 저뿐만이 아니라 많은 분들이 하신 것 같습니다. 커뮤니티에 토론하는 글을 읽으면서 다른 분들의 여러가지 생각들을 들어볼 수 있어서 정말 좋았습니다. </p>
<h3 id="다시-입력하세요">다시 입력하세요.</h3>
<p>요구사항 중 이전 미션들과 다르게 예외가 발생하면 그 부분부터 재입력을 받는 사항이 추가되었습니다. 저는 재입력을 어디에서 담당하는 것이 좋을 지 고민했습니다.
View vs. Controller vs. Service 제가 고민한 담당 후보였습니다.
각각의 장단점을 아래에 적어보겠습니다.</p>
<ul>
<li><strong>View 계층</strong> : 입력 관련 예외처리가 하나에 집중되니 쉽게 이해할 수 있습니다. 하지만, 입력만 받는 <code>InputView</code>에서 예외까지 처리하게 되면 비지니스 로직을 하게 되어 책임이 모호해질 것이라고 판단했습니다.</li>
<li><strong>Controller 계층</strong> : 전체 흐름을 관리하기 때문에 특정 시점에서의 입력을 제어할 수 있습니다. 하지만, Controller의 코드가 복잡해지니 가독성이 떨어질 것이라고 생각했습니다.</li>
<li><strong>Service 계층</strong> : 재입력을 담당하면서 비지니스 로직을 처리하기 떄문에 controller의 코드가 간결해집니다. 하지만, 비지니스 로직에 UI 관련 책임이 추가되기 때문에 단일 책임 원칙을 위반할 수 있습니다.</li>
</ul>
<p>처음에 저는 Controller에서 while문을 사용하여 재입력을 처리했습니다.
근데 코드가 길어지니 가독성이 저하된다고 생각하여 분리하는 것이 좋겠다고 판단했습니다. 그래서 Service 계층에 재입력 책임을 주는 것으로 결정했습니다. </p>
<p>미션 제출 이후 다른 분들의 코드를 보면서 다양한 재입력 방법이 있다는 것을 알게되었습니다. 함수형 인터페이스를 사용하신게 정말 인상깊었습니다 👍</p>
<h2 id="이런-점은-아쉬웠어요">이런 점은 아쉬웠어요..</h2>
<h3 id="미안해-테스트-코드야">미안해.. 테스트 코드야..</h3>
<p>이번에 로직을 구현하느라 목표인 단위 테스트를 신경쓰지 못했습니다.
문득 단순히 객체를 테스트 하는데 의문이 들었습니다. &#39;객체가 잘 생성되는 지까지 불변한지까지 테스트를 과연 해야할까&#39; 그래서 어떤 객체는 테스트를 한 것이 있고 하지 않은 것이 있는 신뢰성 없는 코드가 되어버린 것 같습니다..
4주차 미션에서는 테스트 코드를 신경써서 문서화로서의 기능을 할 수 있게 하겠습니다.</p>
<h3 id="dto-사용-이번엔-놓치지-않을-거예요">DTO 사용, 이번엔 놓치지 않을 거예요!</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/975bab3c-ef5f-4c80-ad59-a794d573ece3/image.png" alt=""></p>
<p>많은 분들이 이번 미션에서 DTO를 사용하신 것을 봤습니다.
저도 이전 리뷰에서 DTO를 적용해보는 것에 대한 제안을 받았었습니다.
그래서 &#39;이번에는 DTO를 꼭 적용시켜야지!&#39; 하고 다짐했지만 어떻게 활용할 지가 명확하지 않아 적용하지 못했습니다. 제목과 다르게 놓쳐버렸습니다.
<del>(이해하고 사용하고 싶은 욕심이 좀 있었습니다!!)</del></p>
<h3 id="급할-수록-차분하게">급할 수록 차분하게</h3>
<p>마지막에 재입력 받는 부분을 급하게 리팩토링 하다가 controller에 있던 주석을...지우지 못하였습니다. 불필요한 import도 제거하지 못한 것 같습니다. 
급하게 PR을 올려야한다는 생각에 너무 지저분한 코드를 제출하게 되었습니다.</p>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/1d489feb-5733-4b9d-9c31-73d6f96e6766/image.png" alt=""></p>
<p><del>(이렇게 걸려버렸습니다. 사실 안걸리는게 이상합니다.. 😂 )</del>
이번 일을 계기로 반성하며 다음에는 무리하게 리팩토링하거나 급하게 휙휙하는 일이 없어야겠다고 생각했습니다.</p>
<h2 id="마지막으로">마지막으로!</h2>
<p>참 마지막까지 많은 일들이 있었습니다. 하지만 리뷰를 주고 받는 과정에서 정말 많은 걸 배울 수 있었던 것 같아요. 서로의 리뷰 뿐만이 아니라 다른 분들의 다양한 리뷰를 보면 &#39;어떻게 저렇게 생각하시지&#39;하는 분들이 저어엉말 많았어요! </p>
<p>이번 4주차 미션을 보고 눈물을 살짝 머금긴했는데 그래도 이번 미션을 하면서 아쉬웠던 부분들, 실수 했던 부분들을 만회한다고 생각하고 열심히 해보려고 합니다!
그리고 이번에도 재입력을 받는 부분이 있더라구요! 그래서 함수형 인터페이스를 공부해서 재입력을 받는 방식을 도전해보려고 합니다. 아자아자!</p>
<p>지금까지 다들 너무너무 고생 많으셨고, 마지막까지 화이팅입니다 🍀</p>
<h2 id="링크">링크</h2>
<p>구경 오셔서 꿀밤 때려주시면 달게 받겠습니다 🙇🏻‍♀️</p>
<blockquote>
<p><a href="https://github.com/woowacourse-precourse/java-lotto-7/pull/642">PR 구경하러 가기!</a></p>
</blockquote>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li><p><a href="https://jaehoney.tistory.com/305">Java - 불변 객체(Immutable Object) 사용을 지향해야 하는 이유</a></p>
</li>
<li><p><a href="https://sungyong.medium.com/%EC%9D%B4%EB%A6%84%EC%A7%93%EA%B8%B0-%EB%B3%80%EC%88%98%EB%AA%85-%ED%95%A8%EC%88%98%EB%AA%85-api%EB%AA%85-e683d5c3405f">이름짓기 — 변수명, 함수명, API명</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 2주차 프리코스 회고]]></title>
            <link>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-2%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-2%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Wed, 30 Oct 2024 07:45:53 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전에">들어가기 전에!</h2>
<h3 id="2주차-피드백">2주차 피드백</h3>
<p>이번 2주차 미션은 더욱 설계를 견고하게 하기로 다짐을 하고 시작했습니다.
이전의 설계를 세심하게 하지 않아 구현하며 많이 수정된 부분이 많았고 이로 인해 혼동이 많았습니다. 그래서 이번 회고에서는 설계 과정과 배운 점을 위주로 작성해보려 합니다!</p>
<h2 id="목표는">목표는!</h2>
<ul>
<li>객체가 어떤 역할과 책임을 가지고 서로 어떤 협력을 하는지 고민하고 설계하자.</li>
<li>강한 결합을 피하고 변경과 확장에 용이한 구조로 구현하자.</li>
<li>MVC 패턴을 사용해보자.</li>
<li>테스트 코드를 짜보자.</li>
<li>완성하자!!</li>
</ul>
<h2 id="자동차-경주">자동차 경주!</h2>
<h3 id="기능-요구-사항">기능 요구 사항</h3>
<p>초간단 자동차 경주 게임을 구현한다.</p>
<ul>
<li>주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다.</li>
<li>각 자동차에 이름을 부여할 수 있다. 전진하는 자동차를 출력할 때 자동차 이름을 같이 출력한다.</li>
<li>자동차 이름은 쉼표(,)를 기준으로 구분하며 이름은 5자 이하만 가능하다.</li>
<li>사용자는 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다.</li>
<li>전진하는 조건은 0에서 9 사이에서 무작위 값을 구한 후 무작위 값이 4 이상일 경우이다.</li>
<li>자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.</li>
<li>우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.</li>
<li>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>을 발생시킨 후 애플리케이션은 종료되어야 한다.</li>
</ul>
<h3 id="실행-결과">실행 결과</h3>
<pre><code class="language-shell">경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
pobi,woni,jun
시도할 횟수는 몇 회인가요?
5

실행 결과
pobi : -
woni : 
jun : -

pobi : --
woni : -
jun : --

pobi : ---
woni : --
jun : ---

pobi : ----
woni : ---
jun : ----

pobi : -----
woni : ----
jun : -----

최종 우승자 : pobi, jun</code></pre>
<h2 id="이렇게-진행했어요">이렇게 진행했어요!</h2>
<h3 id="관련-서적-읽기">관련 서적 읽기</h3>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/b73a5988-5590-4f26-a4db-2d0ad46fb0bc/image.png" width="300" height="300">
객체 지향의 개념으로 구현을 한다고 했지만 ‘객체 지향이 뭐야?’ 라는 질문을 받았을 때 정확한 개념을 설명하는 것이 어려웠습니다. 그래서 관련 서적을 통해 객체 지향에 대해서 알아보기로 했습니다.

<h3 id="적용해보기">적용해보기</h3>
<p>머리로만 익히는 것보다 이번 문제를 통해 실제로 적용시켜 보며 문제를 접근했습니다.</p>
<h4 id="1-어떤-협력이-필요할까">1. 어떤 협력이 필요할까?</h4>
<ul>
<li>사용자가 <strong>자동차 이름과 시도 횟수</strong>를 입력한다.</li>
<li><strong>자동차</strong>는 각자 <strong>이름</strong>과 현재 위치(상태)를 가진다.</li>
<li><strong>진행자</strong>는 여러 번의 시도를 통해 자동차를 이동시킨다.</li>
<li><strong>심판</strong>은 각 자동차의 상태를 기반으로 <strong>누가 우승했는지 발표</strong>한다.</li>
</ul>
<h4 id="2-협력을-위한-행동은-무엇일까">2. 협력을 위한 행동은 무엇일까?</h4>
<ul>
<li>사용자는 <strong>게임을 시작</strong>하고 입력을 한다.</li>
<li>진행자는 <strong>규칙에 맞게 자동차를 이동</strong>시킨다.</li>
<li>각 자동차는 규칙에 따라 <strong>이동</strong>한다.</li>
<li>심판은 자동차들의 상태를 보고 <strong>우승자를 판단</strong>하고 결과를 반환한다.</li>
</ul>
<h4 id="3-행동을-누가-어떤-객체가-수행할까">3. 행동을 누가, 어떤 객체가 수행할까?</h4>
<ul>
<li><strong>사용자</strong>는 &quot;게임을 시작한다&quot;<ul>
<li>게임에 필요한 정보를 입력하고 게임을 시작합니다.</li>
</ul>
</li>
<li><strong>진행자</strong>는 &quot;게임을 진행한다&quot;<ul>
<li>자동차를 이동시키고, 시도 횟수만큼 게임을 진행합니다.</li>
<li>각 라운드마다 자동차들이 이동하도록 요청합니다.</li>
</ul>
</li>
<li><strong>자동차</strong>는 &quot;이동한다&quot;<ul>
<li>규칙에 따라 이동하며, 상태가 업데이트됩니다.</li>
</ul>
</li>
<li><strong>심판</strong>은 &quot;우승자를 뽑는다&quot;<ul>
<li><strong>자동차 상태</strong>를 확인하고 가장 멀리 이동한 자동차를 우승자로 판단합니다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>이러한 생각을 기반으로 각 객체가 무엇을 해야 하는지(Doing)와 무엇을 알아야 하는지(Knowing)를 나누어 정리해봤습니다.</p>
</blockquote>
<h3 id="필요한-객체">필요한 객체</h3>
<h4 id="입력-처리-객체">입력 처리 객체</h4>
<p>Knowing: 입력 값 ( 시도횟수, 이름 )</p>
<p>Doing: 사용자의 입력을 받고, 적절한 형식으로 반환하는 역할</p>
<h4 id="입력-검증-객체">입력 검증 객체</h4>
<p>Knowing: 입력 값</p>
<p>Doing: 사용자의 입력이 유효한지 확인하는 역할</p>
<h4 id="게임-흐름-관리-객체">게임 흐름 관리 객체</h4>
<p>Knowing: 게임의 상태</p>
<p>Doing: 전체 흐름을 관리하는 역할</p>
<h4 id="자동차">자동차</h4>
<p>Knowing: 자신의 이름, 자신의 상태</p>
<p>Doing: 이동하는 역할</p>
<h4 id="자동차-상태를-가지는-객체">자동차 상태를 가지는 객체</h4>
<p>Knowing: 각 자동차의 이름과 상태</p>
<p>Doing: 자동차의 현재 상태를 관리하는 역할 (현재 위치와 이름)</p>
<h4 id="이동-결과를-가지는-객체">이동 결과를 가지는 객체</h4>
<p>Knowing: 자동차의 이동 과정</p>
<p>Doing: 각 자동차의 이동 결과를 기록하는 역할</p>
<h4 id="이동-규칙을-가지는-객체-전략패턴">이동 규칙을 가지는 객체 (전략패턴)</h4>
<p>Knowing: 이동을 위한 규칙</p>
<p>Doing: 규칙을 관리하는 역할</p>
<h4 id="이동을-지시하는-객체">이동을 지시하는 객체</h4>
<p>Knowing: 규칙, 자동차들</p>
<p>Doing: 규칙에 맞게 자동차에게 이동을 명령하는 역할</p>
<h4 id="심판">심판</h4>
<p>Knowing: 최종 이동 결과</p>
<p>Doing: 우승자를 판단하는 역할</p>
<h4 id="결과-출력-객체">결과 출력 객체</h4>
<p>Knowing: 우승자와 실행 결과</p>
<p>Doing: 우승자와 실행과정을 출력하는 역할</p>
<blockquote>
<p>그래서 제가 생각한 입력 규칙은 아래와 같습니다! </p>
</blockquote>
<p><strong>[이름]</strong></p>
<ul>
<li>5자 이하여야 합니다. ex) pobii (x)</li>
<li>영어로만 이루어져야 합니다. ex) 우테코, pob2 (x)</li>
<li>중복된 이름은 사용할 수 없습니다. ex) woni, woni (x)</li>
<li>이름은 쉼표로 구분됩니다.<blockquote>
</blockquote>
</li>
<li>*[시도 횟수]**</li>
<li>숫자만 입력할 수 있습니다. ex) aa (x)</li>
<li>1부터 10까지만 입력할 수 있습니다. ex) 11 (x)</li>
</ul>
<h3 id="구현">구현</h3>
<p>아래는 PR 링크입니다! 아직 부족한 부분이 많지만 리뷰는 언제나 환영입니다 🙇🏻‍♀️</p>
<blockquote>
<p><a href="https://github.com/woowacourse-precourse/java-racingcar-7/pull/1185">코드 보러가기 👀</a></p>
</blockquote>
<h3 id="흐름도">흐름도</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/e5adc4d8-927a-4423-8aea-ebab8057e146/image.png" alt=""></p>
<h2 id="알게됐어요">알게됐어요!!!</h2>
<h3 id="mvc-패턴">MVC 패턴</h3>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/dddd2d83-b8b4-4f9c-a350-12450f17608c/image.png" alt=""> 출처:<a href="https://developer.mozilla.org/ko/docs/Glossary/MVC">MVC 패턴</a></p>
<blockquote>
<p>MVC 소프트웨어 디자인 패턴의 세 가지 부분은 다음과 같이 설명할 수 있습니다.</p>
</blockquote>
<ul>
<li>모델: 데이터와 비즈니스 로직을 관리합니다.</li>
<li>뷰: 레이아웃과 화면을 처리합니다.</li>
<li>컨트롤러: 모델과 뷰로 명령을 전달합니다.</li>
</ul>
<h4 id="블로그의-게시물을-예시로-들어보면">블로그의 게시물을 예시로 들어보면,</h4>
<ol>
<li>사용자가 제목과 본문을 입력하고 &quot;작성하기&quot; 버튼을 클릭합니다. </li>
<li>컨트롤러(Controller)는 입력한 정보를 받아 &quot;데이터베이스에 저장해&quot;하고 모델에게 전달합니다.</li>
<li>모델(Model)은 전달 받은 게시글 데이터를 데이터베이스에 저장한 후, 컨트롤러에 알려줍니다.</li>
<li>컨트롤러에서는 게시글 작성이 완료된 것을 확인하고, 사용자에게 게시글 목록으로 이동하게 지시합니다.</li>
<li>뷰(View)는 게시글 목록 데이터를 화면에 표시하고, 자신이 작성한 게시글을 확인할 수 있게 합니다.</li>
</ol>
<p>이렇게 각각의 요소가 자신만의 역할을 수행하기 때문에, 특정 부분을 수정하거나 업데이트해야 할 때 관련된 역할에만 집중하면 됩니다. 이를 통해 코드의 유지보수가 쉬워지고, 역할이 명확히 나뉘어 더 체계적인 설계를 할 수 있습니다.</p>
<blockquote>
<p>MVC 패턴을 이해할 때 도움이 된 블로그입니다! - <a href="https://velog.io/@langoustine/%EC%97%AC%EA%B8%B0%EB%8F%84-MVC-%EC%A0%80%EA%B8%B0%EB%8F%84-MVC-MVC-%ED%8C%A8%ED%84%B4%EC%9D%B4-%EB%AD%90%EC%95%BC">MVC 패턴이 뭐야</a></p>
</blockquote>
<h3 id="전략-패턴">전략 패턴</h3>
<blockquote>
<p><strong>전략 패턴</strong>은 알고리즘들의 패밀리를 정의하고, 각 패밀리를 별도의 클래스에 넣은 후 그들의 객체들을 상호교환할 수 있도록 하는 행동 디자인 패턴입니다.</p>
</blockquote>
<pre><code class="language-java">public interface MoveStrategy {
    boolean canMove();
}


public class RandomMoveStrategy implements MoveStrategy {

    private static final int MIN = 0;
    private static final int MAX = 9;
    private static final int MOVE_THRESHOLD = 4;

    @Override
    public boolean canMove() {
        int randomValue = Randoms.pickNumberInRange(MIN,MAX);

        return randomValue &gt;= MOVE_THRESHOLD;
    }

}</code></pre>
<p>저는 자동차의 이동 규칙을 유연하게 적용할 수 있도록 <code>MoveStrategy</code> 인터페이스를 통해 이동 여부를 결정하는 로직을 추상화했습니다. 이 방식은 추후 새로운 이동 규칙이 필요하거나 기존 규칙을 변경할 때 다른 코드에 영향을 주지 않으므로 유지보수가 용이하다는 장점이 있습니다. 또한, 전략 패턴 덕분에 자동차 외의 다른 객체가 이동 규칙을 필요로 할 때도 같은 구조를 재사용할 수 있습니다.</p>
<h2 id="고민했어요">고민했어요!</h2>
<h3 id="입력-값-검증은-누가">입력 값 검증은 누가?</h3>
<p>입력값을 검증하는 클래스를 따로 분리할지, 객체가 직접 검증하도록 할지 고민했습니다. 
검증 클래스를 분리하면 검증 로직을 재사용하기 쉬워지고, 코드가 깔끔해지며 단일 책임 원칙을 충족하는 장점이 있습니다.
반면 객체가 직접 검증하게 되면, 해당 객체가 스스로 데이터의 일관성을 관리하므로 결합도가 낮아지고 유지보수에 유리할 수 있습니다.
단일 책임 원칙을 지키기 위해서 저는 입력값 검증 클래스를 분리해서 사용하기로 결정하였습니다.</p>
<h3 id="시도횟수는-문자열로-숫자로">시도횟수는 문자열로? 숫자로?</h3>
<p>시도 횟수를 검증할 때, 문자열로 받아 숫자로 변환할지, 숫자만 받도록 할지 고민했습니다. 
최종적으로 문자열로 받는 방식을 선택했습니다. <code>int</code>로 받을 경우 타입이 고정되어 안정적이지만, 문자열 입력 시 예외 처리가 필요합니다. <code>String</code>으로 받을 경우 변환 과정이 추가되지만, 다양한 형식으로 입력한 값을 더 쉽게 처리할 수 있습니다. 
저는 예외를 처리하는 과정이 가독성을 해칠 수 있다고 생각하여 문자열로 받는 것이 더 효과적일 것이라 판단했습니다.</p>
<h3 id="테스트-코드는-어디까지">테스트 코드는 어디까지?</h3>
<p>테스트 코드 작성 시, 어느 범위까지 테스트를 수행해야 할지 고민했습니다. 
모든 객체를 개별적으로 테스트해야 할지, 주요 로직과 상호작용을 중심으로 테스트를 구성해야 할지에 대한 고민이었습니다. 
초기에는 각 객체의 메서드 단위로 테스트하는 것이 좋다고 생각했지만, 점차 객체 간의 협력이 중요한 만큼 주요 기능이 잘 작동하는지를 중심으로 통합 테스트를 강화하는 방향으로 방향을 잡았습니다. 이를 통해 단위 테스트와 통합 테스트를 적절히 조화시켜야 한다고 느꼈습니다.</p>
<h2 id="마지막으로">마지막으로!</h2>
<p>이번 미션을 하면서 아쉬웠던 점은 객체들이 자신의 역할을 가지면서 협력하는 것을 목표로 하였지만 설계한대로 코드가 구현되었는 지에 대한 확신이 들지 않았습니다. 너무 역할과 책임, 협력에 초점을 둔 것이 아닐까하는 생각도 들었습니다.
이전 리뷰에서 &#39;일급 컬렉션&#39;,&#39;불변 객체&#39;에 대해 피드백을 받아 적용시켜보려 노력했지만, 아직 완전히 이해하지 못해서 부족한 부분도 있는 것 같습니다.
하지만 이러한 경험이 많은 것들을 배울 수 있는 좋은 기회였다고 생각합니다.
다음 미션에서는 위의 개념들을 학습하고 아쉬웠던 부분을 보완할 수 있도록 노력할 것입니다!
긴 글 읽어주셔서 감사합니다 😊</p>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Glossary/MVC">MVC 패턴</a></li>
<li><a href="https://refactoring.guru/ko/design-patterns/strategy">전략 패턴</a></li>
<li><a href="https://tech.inflab.com/20230404-test-code/">테스트 코드를 왜 그리고 어떻게 작성해야 할까?</a></li>
<li><a href="https://yozm.wishket.com/magazine/detail/1964/">테스트 코드는 왜 만들까</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[우아한테크코스 7기] 1주차 프리코스 회고]]></title>
            <link>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-1%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC%EC%BD%94%EC%8A%A4-7%EA%B8%B0-1%EC%A3%BC%EC%B0%A8-%ED%94%84%EB%A6%AC%EC%BD%94%EC%8A%A4-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 22 Oct 2024 18:33:16 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가기-전에">들어가기 전에!</h2>
<p>3시에 미션이 나오고 기대 반 걱정 반으로 문제를 읽었습니다. 처음에는 &#39;읭..? 생각보다 간단한 것 같은데 ?&#39; 라고 생각했는데 그러다가 눈물 펑펑 흘렸습니다.
이번 1주차 회고에서는 어떤 목표를 가지고 어떻게 접근했는지, 목표를 달성했다고 생각하는지 적어보려 합니다.</p>
<h2 id="제-목표는요">제 목표는요!</h2>
<ol>
<li>객체 지향을 준수해서 각 클래스와 메서드가 한 가지의 역할을 하게 하자.</li>
<li>다른 사람들이 봤을 때 &#39;아 이런 역할을 하는 메서드구나&#39;하고 이해할 수 있게 메서드명을 명확히 하자.</li>
<li>코드 한 줄 한 줄 이유를 생각하고 구현하자.</li>
<li>완성하자.</li>
</ol>
<h2 id="문제는요">문제는요!</h2>
<h3 id="문자열-덧셈-계산기"><strong>[문자열 덧셈 계산기]</strong></h3>
<h3 id="기능-요구사항">기능 요구사항</h3>
<p>입력한 문자열에서 숫자를 추출하여 더하는 계산기를 구현한다.</p>
<p>쉼표(,) 또는 콜론(:)을 구분자로 가지는 문자열을 전달하는 경우 구분자를 기준으로 분리한 각 숫자의 합을 반환한다.
    ◦ 예: <code>&quot;&quot; =&gt; 0, &quot;1,2&quot; =&gt; 3, &quot;1,2,3&quot; =&gt; 6, &quot;1,2:3&quot; =&gt; 6</code></p>
<p>앞의 기본 구분자(쉼표, 콜론) 외에 커스텀 구분자를 지정할 수 <strong>있다.</strong> 커스텀 구분자는 문자열 앞부분의 &quot;//&quot;와 &quot;\n&quot; 사이에 위치하는 문자를 커스텀 구분자로 사용한다.
    ◦ 예를 들어 <code>&quot;//;\n1;2;3&quot;</code>과 같이 값을 입력할 경우 커스텀 구분자는 세미콜론(;)이며, 결과 값은 6이 반환되어야 한다.</p>
<p>사용자가 잘못된 값을 입력할 경우 <code>IllegalArgumentException</code>을 발생시킨 후 애플리케이션은 종료되어야 한다.</p>
<h3 id="입출력-요구-사항">입출력 요구 사항</h3>
<p><strong>입력</strong> : 구분자와 양수로 구성된 문자열</p>
<pre><code class="language-java">1,2,3</code></pre>
<p><strong>출력</strong> : 덧셈 결과</p>
<pre><code class="language-java">결과 : 6</code></pre>
<h3 id="실행-결과-예시"><strong>실행 결과 예시</strong></h3>
<pre><code class="language-java">덧셈할 문자열을 입력해 주세요.
1,2:3
결과 : 6</code></pre>
<h2 id="이렇게-진행했어요">이렇게 진행했어요!</h2>
<h3 id="문제-파악">문제 파악</h3>
<p>노션에 문제를 읽으면서 생각했던 것들을 다 적었습니다. 처음엔 구분자의 정확한 정의가 무엇인지 궁금해서 찾아봤습니다.</p>
<blockquote>
<p>구분자는 데이터를 구분하거나 분리하는 데 사용하는 문자 또는 기호이다.</p>
</blockquote>
<p>기본 구분자는 이미 정해져 있기 때문에 문자열을 나누는데에 어려움이 없었고, 커스텀 구분자는 정해져 있지 않기에 어떤 기준으로 사용할 수 없는 구분자를 정해야 할지 생각했습니다.
그래서 정해진 저의 규칙은 아래와 같습니다. 아래 규칙 이외 문자열은 사용이 가능하게 하였습니다. </p>
<blockquote>
<h4 id="커스텀-구분자-규칙">커스텀 구분자 규칙</h4>
</blockquote>
<ol>
<li>//와 \n는 커스텀 구분자를 정하기 위한 형식이므로 혼동을 줄 수 있기 때문에 사용할 수 없다.</li>
<li>공백 : 공백으로 이루어진 구분자는 사용할 수 없다. </li>
<li>숫자 : 숫자를 계산해야 하기 때문에 구분자로 사용할 수 없다.</li>
</ol>
<h3 id="역할-분리">역할 분리</h3>
<p>한 가지 역할의 목표를 위해 어떤 역할이 필요한 지 먼저 나누었습니다.</p>
<ul>
<li>문자열을 입력받고 결과를 출력하는 역할</li>
<li>입력받은 문자열을 검증하는 역할</li>
<li>문자열에서 구분자를 찾는 역할</li>
<li>구분자로 나눈 후 숫자를 찾는 역할</li>
<li>덧셈 연산하는 역할</li>
</ul>
<h3 id="테스트-예제">테스트 예제</h3>
<p>여러 예제를 고민하며 생각날 때마다 테스트 예제를 추가해서 정리했습니다.
이렇게 고민하니 추가적으로 어디서 예외 처리를 하면 좋을 지 알 수 있었던 것 같습니다.</p>
<table>
<thead>
<tr>
<th>입력값</th>
<th>기댓값</th>
</tr>
</thead>
<tbody><tr>
<td>“”</td>
<td>0</td>
</tr>
<tr>
<td>1,2,3</td>
<td>6</td>
</tr>
<tr>
<td>1,2:3</td>
<td>6</td>
</tr>
<tr>
<td>//;\n1;2:3,4</td>
<td>10</td>
</tr>
<tr>
<td>//;\n1</td>
<td>1</td>
</tr>
<tr>
<td>//;\n1;2;</td>
<td>3</td>
</tr>
<tr>
<td>:1:2:3</td>
<td>6</td>
</tr>
<tr>
<td>//<em>\n1</em>2*3</td>
<td>6</td>
</tr>
<tr>
<td>1::2,3</td>
<td>6</td>
</tr>
<tr>
<td>//;\n1;;2;3</td>
<td>6</td>
</tr>
<tr>
<td>//;\n//&amp;\n1;2&3;4</td>
<td>10</td>
</tr>
<tr>
<td>//\n123</td>
<td>사용할 수 없는 커스텀 구분자 사용 예외 발생</td>
</tr>
<tr>
<td>\n1;2;3</td>
<td>커스텀 구분자 형식 예외 발생</td>
</tr>
<tr>
<td>-1,2,3</td>
<td>구분자로 나누었을 때 음수 예외 발생</td>
</tr>
<tr>
<td>//<em>\n1</em>a*3</td>
<td>구분자로 나누었을 때 문자열 예외 발생</td>
</tr>
</tbody></table>
<h3 id="구현">구현</h3>
<p>구현한 내용은 깃허브 PR 링크를 걸어두겠습니다!
만약.. 누가 본다면 리뷰도 환영입니다 :)</p>
<blockquote>
<p><a href="https://github.com/woowacourse-precourse/java-calculator-7/pull/1063">코드 구경 하러 가기 👀</a></p>
</blockquote>
<h2 id="제가-배운-건요">제가 배운 건요!</h2>
<h3 id="정규식">정규식</h3>
<p>숫자와 구분자를 많이 다뤄야하는 문제이다보니 정규식은 빠질 수 없다고 생각했습니다. 저는 숫자나 정해진 문자열만 정규식을 사용하려 하였습니다. 이유는 정규식은 정말 다양하고, 형식이 정해져 있는 것도 있어 편리하지만 저 조차도 이게 어떤 걸 표현하는 정규식인지 한 눈에 알아보기 어려웠기 때문입니다. 아직 정규식에 대해서 잘 모르는 다른 사람이 봤을 때도 어떤 정규식인지 알아볼 수 있는 것만 사용했습니다. 아래는 제가 정규식을 공부할 때 도움을 받았던 블로그입니다!</p>
<blockquote>
<p><a href="https://adjh54.tistory.com/104#1.%20%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D%20%ED%8C%A8%ED%84%B4(Regular%20Expression%20Pattern)-1">정규 표현식 이해하기</a></p>
</blockquote>
<h3 id="여러-상황-고려">여러 상황 고려</h3>
<p>처음에는 기본 구분자와 커스텀 구분자를 추출하고 그걸로 정규식을 만들어서 구분하면 될 것이라고 생각했습니다. 하지만 &#39;나중에 기본 구분자가 추가된다면? 커스텀 구분자로 사용할 수 없는 게 추가된다면? 커스텀 구분자를 나누는 형식을 바꾸고 싶으면 어떻게 하지?&#39; 라는 생각을 하게 되었습니다.
이렇게 다양한 상황을 생각하는 것이 코드를 유지보수할 때 불편한 상황을 빠르게 파악할 수 있다는 것을 깨닫게 해주었습니다.</p>
<h3 id="enum을-효율적으로">Enum을 효율적으로</h3>
<p>열거된 상수를 사용해서 이름을 명확하게 나타낼 수 있다는 장점을 이용하여 기본 구분자와 사용할 수 없는 커스텀 구분자를 Enum 클래스로 만들었습니다. 하지만 선언한 상수를 확인하기 위해서 조건문과 반복문이 필요했고 &#39;뭔가 더 좋게 활용할 수 있을 것 같은데..&#39; 라는 생각이 들었습니다. 그래서 Enum 클래스에 대해서 알아보고 메서드를 사용하니 더 깔끔한 코드를 짤 수 있었습니다. <del>(엄청난 효과를 봤습니다!!!)</del></p>
<pre><code class="language-java">// 수정 전 코드
...
private boolean containsValidDelimiter(String input) {
    for (Delimiter delimiter : Delimiter.values()) {
        if (delimiter == Delimiter.COMMA || delimiter == Delimiter.COLON) {
            if (input.contains(delimiter.getDelimiter())) {
                return true;
            }
        }
    }
    return false;
}
...

// 수정 후 코드
...
private boolean containsValidDelimiter(String input) {
    return Delimiter.getBasicDelimiters()
                    .stream()
                    .anyMatch(input::contains);
}
...
</code></pre>
<blockquote>
<p><a href="https://techblog.woowahan.com/2527/">Java Enum  활용기 - 우아한기술블로그</a></p>
</blockquote>
<h3 id="util과-service">Util과 Service</h3>
<p>여러 PR을 구경하니 MVC를 사용하신 분들이 많았습니다. 저는 MVC를 쓰지 않아서 패키지 구조에 대한 고민을 하게 되었습니다.
처음에는 <code>controller</code>를 제외한 모든 클래스를 <code>service</code>에 포함해야 한다고 생각했지만, 구분자와 숫자를 추출하는 기능이 상태를 가지거나 의존성을 필요로 하지 않는다는 점에서 <strong>빈 등록이 불필요</strong>하다고 판단했습니다.
이렇게 분리한 덕분에 필요에 따라 재사용이 가능해졌습니다.
하지만 단순히 공통 기능을 <code>util</code>로 묶는 것보다, 책임과 역할에 맞게 적절히 분리하는 것이 중요하다는 점을 깨달았고 너무 광범위하게 사용할 경우 단일 책임 원칙에 어긋날 수 있기 때문에, <code>util</code>의 활용은 신중하게 결정해야 한다는 것을 배울 수 있었습니다.</p>
<blockquote>
<p>공부하면서 참고했던 블로그입니다!
<a href="https://curiousjinan.tistory.com/entry/util-class-static-vs-spring-bean">Util 클래스</a></p>
</blockquote>
<h2 id="저의-느낀-점은요">저의 느낀 점은요!</h2>
<p>앞서 적었던 목표를 달성했는지 생각해보면 아쉬웠던 부분이 많은 것 같습니다.
역할을 분리해놨음에도 계속 해서 중복되는 역할을 가지거나 여러 역할을 가지고 있는 것들도 있는 것 같습니다.
객체 지향에 대해서 완벽히 이해하지 않고 역할에만 초점을 두니 이런 일이 일어나지 않았나 생각합니다..🥺
메서드명은 하는 역할을 명확하게 명시하기 위해서 노력했습니다.
생각보다 메서드명 짓기가 어렵다는 걸 알게되었습니다.<del>(제가 영어에 약한걸지도..)</del>
만약 코드를 보다가 &#39;얘가 무슨 역할을 하는 거지..?&#39; 하는 생각이 든다면 바로 혼내주시면 감사하겠습니다! 
이번 미션을 하면서 다양한 지원자분들의 PR도 구경을 많이 했습니다. 보면서 대단하다고 생각하신 분들이 많았습니다.
그런 분들과 모여서 소통하며 많이 배워갈 수 있을 거라고 생각하니 정말 기대가 됩니다!</p>
<h2 id="다음엔-이렇게-할게요">다음엔 이렇게 할게요!</h2>
<p>객체 지향에 대해서 요즘 많이 알아보고 관련 서적도 읽어보고 있습니다. 추천해주셔도 좋습니다!!
이번 2주차 미션에서는 1주차 때 아쉬웠던 부분을 보완해서 객체 지향적이고 더 나은 코드를 위해 최선을 다하겠습니다! 화이팅👊🏻</p>
]]></description>
        </item>
        <item>
            <title><![CDATA['TITTO' 프로젝트 회고]]></title>
            <link>https://velog.io/@dd-jiyuni/TITTO-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@dd-jiyuni/TITTO-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 26 May 2024 15:06:46 GMT</pubDate>
            <description><![CDATA[<h2 id="🗓️-프로젝트-기간">🗓️ 프로젝트 기간</h2>
<p><code>2024. 1. 10</code> ~ <code>2024. 04. 01</code></p>
<h2 id="💬-프로젝트-설명">💬 프로젝트 설명</h2>
<p>교내 멘토-멘티 프로그램을 활성화 하기 위한 커뮤니티 플랫폼입니다.</p>
<p>많이 사용되는 대학 생활 커뮤니티는 익명성으로 인해 전공과 관련된 질문의 정확한 답변 뿐만 아니라 교내 프로그램을 적극적으로 참여하기에 어려움을 겪는다고 생각했습니다.</p>
<p>위와 같은 문제를 개선하는 것을 목표하여 멘<strong>&#39;토&#39;</strong> 와 멘<strong>&#39;티&#39;</strong> &#39;<strong>TITTO</strong>&#39; 프로젝트를 진행하게 되었습니다.</p>
<h2 id="👥-팀-구성">👥 팀 구성</h2>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/f842bce1-3302-4cf2-baf4-55b5073a04ae/image.png" alt=""></p>
<h2 id="⚒️-프로젝트-사용-기술">⚒️ 프로젝트 사용 기술</h2>
<h3 id="-백엔드-">[ 백엔드 ]</h3>
<ul>
<li>Java + Spring boot</li>
<li>Spring Data JPA</li>
<li>Spring Security + JWT</li>
<li>OAuth 2.0</li>
<li>AWS EC2</li>
<li>Redis</li>
<li>MySQL</li>
</ul>
<h3 id="-프론트엔드-">[ 프론트엔드 ]</h3>
<ul>
<li>React</li>
<li>TypeScript</li>
<li>AWS</li>
</ul>
<h2 id="💧-아쉬웠던-점">💧 아쉬웠던 점</h2>
<h3 id="erd-설계">ERD 설계</h3>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/5c582231-68b4-41c8-9078-036820fa9328/image.png" alt="erd">

<p>프로젝트 아이디어가 확정된 후, 백엔드 팀원과 함께 ERD(엔티티-관계 다이어그램) 설계를 진행하였습니다.</p>
<p>초기에 ERD를 체계적으로 설계하는 것은 테이블 간 어떤 데이터를 주고 받는지를 명확히 이해하고, 팀원 간의 원활한 소통은 물론, 나중에 기능을 구현할 때도 큰 도움이 됩니다.</p>
<p>그러나 프로젝트 진행 과정에서 Spring Data JPA를 이용하여 테이블을 생성하게 되면서, 시간이 지나면서 사전에 설계했던 ERD를 참조하지 않게 되었습니다. </p>
<p>추가적으로 필요한 컬럼이 생길 때마다 즉흥적으로 추가하고, 테이블 간의 매핑 관계에 대해서는 충분한 고민을 하지 않았습니다. </p>
<p>이로 인해 유저 테이블이 모든 정보를 담고 있게 되었고, 각 테이블들이 어떤 데이터를 보유하고 있는지 명확하지 않게 되는 상황이 발생했습니다.</p>
<h3 id="spring-data-jpa-사용-경험">Spring Data JPA 사용 경험</h3>
<p><code>Spring Data JPA</code>를 사용하면서 여러 <code>Query Method</code>를 지원하여 데이터베이스 조회를 간편하게 할 수 있다는 장점을 느꼈습니다. 
그러나 조건이 많아질 경우 메서드 명이 너무 길어져 <strong>가독성이 떨어진다</strong>는 단점이 있었습니다.</p>
<p>또한, JPA를 사용하면서 주의해야 할 점 중 하나인 <strong>N+1 문제</strong>를 고려하지 못했던 것이 아쉬웠습니다. 
<strong>N+1 문제</strong>는 연관된 엔티티들을 조회할 때, 불필요하게 많은 쿼리가 발생하는 문제로, 성능 저하를 초래할 수 있습니다. </p>
<p>이를 방지하기 위해 <code>FetchType</code>을 조정하거나, <code>JPQL</code>이나 <code>QueryDSL</code>을 사용해 쿼리를 최적화할 수 있는 방법에 대해서 공부해야겠다고 생각했습니다.</p>
<h3 id="협업-및-소통-방식">협업 및 소통 방식</h3>
<p>백엔드 팀원 간 기능 분담을 통해 프로젝트를 진행했습니다.</p>
<p>협업 도구인 GitHub을 사용하면서 코드 컨벤션과 커밋 컨벤션을 사전에 명확히 정하지 않아 나중에 이를 변경하려 할 때 많은 번거로움이 있었습니다.</p>
<p>또한, 기능이 완성되고<code>메인 브랜치</code>에 커밋을 올리는 과정에서 리뷰를 생략하고 바로 병합하는 식으로 진행했기 때문에 코드 충돌이 자주 발생했습니다.</p>
<p>이러한 문제를 인식하고 나서는 시간적 제약으로 인해 리뷰를 제대로 수행하기 어려웠고, 결국 노션을 활용하여 구현 및 수정 사항을 기록하는 방식으로 리뷰를 대체해야 했습니다.</p>
<p>프론트엔드와의 연동 과정에서 발생한 오류를 처리하는 과정에서도, 문제를 신속히 해결할 수 있는 사람이 해당 오류를 수정하고, 수정 사항을 확인하는 데 필요한 시간을 할애하는 등 효율적이지 못한 소통 방식이 이루어졌습니다. </p>
<p>특히 프론트엔드에서 오류가 발생했을 때, 즉각적인 해결을 위해 일단 기능이 작동하도록 구현하는 과정에서 서로 수정 방안에 대해 충분히 논의하지 않아 더 나은 해결책을 찾지 못하는 상황이 반복되었습니다.</p>
<h3 id="시간-분배">시간 분배</h3>
<p>저는 3개월 간의 프로젝트 기간은 충분히 길었다고 생각합니다.</p>
<p>각 기능의 완성에 대한 마감일을 설정하지 않았습니다. </p>
<p>마감일을 설정하지 않아, 프론트엔드 팀이 이미 화면을 완성했음에도 불구하고 API 연동을 할 수 없는 상황이 발생했습니다.</p>
<p>또한, 세부적으로 기획했다고 생각했으나, 중간중간에 &quot;이 기능을 추가할까, 아니면 이 기능을 빼볼까?&quot;하는 의견이 오고 갔습니다. 이미 완성된 기능을 수정할 여유도 없이 새로운 기능만을 추가했습니다. </p>
<p>그 결과, 프로젝트 시작 전에 목표했던 깔끔하고, 누가 봐도 어떤 기능을 하는 로직인지 알 수 있는 코드를 구현하지 못했습니다.</p>
<p>경진대회 제출을 앞두고도 계속 수정이 이루어졌습니다. </p>
<p>서비스를 조금 더 일찍 완성하고, 시연하는 과정에서 오류를 꼼꼼히 잡아냈다면, 좀 더 탄탄한 프로젝트가 되었을 것이라 생각합니다.</p>
<h3 id="소셜-로그인">소셜 로그인</h3>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/ad8ee805-b624-43ce-84aa-cdd15a870479/image.png">

<p>저희는 카카오를 통해 편리하게 로그인한 후, 사용자에게 추가적인 정보를 입력받는 방식을 사용했습니다.</p>
<p>현재 구현 방식에서는 카카오 로그인 후에 카카오로부터 요청한 정보만 데이터베이스에 저장합니다. 이후 사용자에게 추가 정보를 입력받고 데이터베이스를 업데이트하는데, 이 과정에서 총 <strong>두 번의 쿼리</strong>가 발생합니다.</p>
<p>이 점을 개선하기 위해 몇 가지 방법을 생각해보았습니다.</p>
<p>첫째, 사용자의 가입 상태를 관리하는 것입니다. <code>Enum</code> 타입을 사용해 가입 상태를 파악하고, 모든 정보가 채워지지 않은 사용자에게는 추가 정보 입력 페이지를 띄우는 방식입니다.</p>
<p>둘째, 카카오 로그인 시 받은 사용자 정보를 바로 데이터베이스에 저장하는 대신, <strong>캐시나 세션</strong>에 임시로 저장해 놓고 추가 정보를 입력받은 후에 모든 정보를 한 번에 데이터베이스에 저장하는 것입니다. 이렇게 하면 총 두 번의 쿼리를 한 번으로 최적화할 수 있습니다.</p>
<p>현재 방식은 카카오 로그인만 하면 JWT 토큰을 발급해주었지만, 위의 방식을 이용해 모든 정보가 입력된 경우에만 토큰을 발급하면 <strong>보안상으로도 이점</strong>이 있을 것입니다.</p>
<h2 id="💡-배운-점">💡 배운 점</h2>
<h3 id="철저하게-기획하자">철저하게 기획하자</h3>
<p>아이디어를 기획할 때 필요한 기능과 추후에 추가될 수 있는 기능을 정확하게 문서화하고 이에 대비할 수 있어야 합니다. </p>
<p>기능 명세서를 작성하여 현재 필요한 기능과 향후 확장 가능성을 명확히 정의하고, 처음부터 확장성을 염두에 둔 설계를 통해 새로운 기능을 추가할 때 기존 코드에 최소한의 영향을 미치도록 합니다.</p>
<p>또한, ERD를 구상할 때 JPA에만 의존하는 것이 아니라 직접 테이블의 연관 관계를 명확히 정의하고, 이로 인해 발생할 수 있는 성능 문제를 미리 예측하여 대비해야 합니다. </p>
<p>쿼리 최적화, 인덱스 사용, 캐싱 등을 통해 데이터베이스와 애플리케이션의 성능을 최적화하고, 유지보수 측면에서도 문제가 없는지 파악하며 진행해야 합니다.</p>
<h3 id="프로젝트-초반에-규칙을-정하자">프로젝트 초반에 규칙을 정하자</h3>
<p>모든 팀원과 함께 코드를 공유하고 구현하기 때문에 <strong>코드 컨벤션</strong>과 <strong>브랜치 전략</strong>, <strong>커밋 컨벤션</strong> 등 코드 규칙에 대한 것들을 정해놓는 것이 좋습니다.</p>
<p>각 파트끼리 역할을 나눠 기능을 구현한다면 작성한 코드를 서로 <strong>리뷰</strong>하며 의견을 나누는 것이 협업에서 얻어갈 수 있는 가장 큰 장점이라고 생각합니다.</p>
<p>프로젝트의 마감 기한을 정하고 아이디어 구체화 단계, 개발 단계, 유지 보수 단계 등을 나누어 정확하게 일정을 관리해야 더 성공적인 프로젝트를 완성할 수 있습니다.</p>
<h3 id="api-문서-자동화">API 문서 자동화</h3>
<p>저는 <code>Swagger UI</code>를 사용하여 API 문서를 작성하였습니다.</p>
<p>처음에는 노션을 이용해 API 명세서를 작성하여 프론트엔드와 협업을 진행했습니다. 그러나 이러한 방식은 API가 추가될 때마다 필요한 데이터를 포함하여 명세서를 업데이트하는 번거로움이 있었습니다.</p>
<p>그래서 우리는 다양한 어노테이션을 추가하면 자동으로 문서화해주는 Swagger를 사용하게 되었습니다. Swagger는 <strong>쉽게 설정</strong>할 수 있고, <strong>편리한 UI</strong>를 제공하여 프론트엔드 팀도 한눈에 확인할 수 있다는 장점이 있었습니다.</p>
<p>또한, 도메인을 구매하고 HTTPS를 사용하여 기존의 <code>http://{ip 주소}</code> 배포 주소 대신 <code>https://{도메인 주소}</code>를 사용함으로써 보안성과 신뢰성을 높였습니다.</p>
<p>하지만 여러 가지 어노테이션을 추가하면서, 각 기능의 요청마다 반환되는 값이 통일되지 않아 코드의 가독성이 떨어지는 문제점이 발생했습니다.</p>
<p>이 문제를 해결하기 위해 Swagger를 대체할 수 있는 API 문서 자동화 도구를 찾던 중 <code>Spring REST Docs</code>를 알게 되어 프로젝트에 적용할 수 있을지 검토해보았습니다.</p>
<p><code>Spring REST Docs</code>는 테스트 코드를 기반으로 문서를 작성해줍니다. 이로 인해 API 문서의 정확성과 신뢰도가 높아지는 장점이 있습니다.</p>
<p>하지만 저희 프로젝트는 TDD 방법으로 진행하지 않았고, 이를 학습하여 적용하기에는 시간이 많이 소요될 것 같아 결국 Swagger를 계속 사용하기로 결정했습니다.</p>
<h2 id="🗣️-팀원들의-피드백">🗣️ 팀원들의 피드백</h2>
<p><img src="https://velog.velcdn.com/images/dd-jiyuni/post/2e42b855-7cab-49b4-99c7-dc0403336eae/image.png" alt=""></p>
<p>위와 같이 팀원들에게 프로젝트가 마무리 된 후 피드백을 요청하였습니다.피드백 내용을 정리해보니 비슷한 문제점 찾을 수 있었습니다.</p>
<p><strong>코드 컨벤션</strong>을 백엔드 파트에서 맞춰주지 않아 API를 연동하는 과정에서 많이 힘들었다는 것이었습니다.</p>
<p>저 또한 아쉬웠던 부분이었는데 프론트 분들의 피드백을 들으니 더욱 와닿았습니다 🥲</p>
<p>마지막으로, 팀원분들 모두 좋았던 부분과 부족했던 부분들을 잘 말씀해주셔서 정말 감사했습니다 🙇🏻‍♀️</p>
<h2 id="👀-마치며">👀 마치며..</h2>
<p>3개월 동안 프로젝트를 하면서 다양한 점들을 배울 수 있었던 것 같습니다.</p>
<p>저도 부족한 점이 많았을 텐데 팀원분들 덕에 마지막까지 잘 마무리 할 수 있었던 것 같습니다!</p>
<p>이번 프로젝트에서 아쉬웠던 부분들을 다음에는 좀 더 신경써서 진행해야겠다는 생각을 하였습니다.</p>
<h2 id="📄-아키텍쳐">📄 아키텍쳐</h2>
<img src="https://velog.velcdn.com/images/dd-jiyuni/post/5ac6f43d-c030-4212-8e63-4eddc4db619a/image.png" alt="architecture">


<h2 id="🔗-프로젝트">🔗 프로젝트</h2>
<blockquote>
<p> <a href="https://github.com/SKHU-TITTO/TittoBackend">Github 보러가기</a>
   <a href="https://www.figma.com/design/bespMbQS0xJlfC1lYVAaYH/Titto?node-id=0-1&amp;t=Y9vDliAeEcSqKvCS-0">Figma 구경가기</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>