본문 바로가기

Loopers/WIL

Week 2. 설계는 선택의 연속이다.

화면 개발 기획서를 받아 볼 때마다
데이터를 어떻게 긁어와 보여줄지 쿼리부터 생각하곤 한다.

 

체계적으로 문서화가 되어 있는 게 아니다 보니 경험에 많이 의존하는 편이다.

 

요구사항 분석 > DB 설계 > 서비스 API 설계 > 구현 순으로 진행되며,
분석/설계와 구현의 비율이 대강 6:4정도 되는 듯하다.

 

그렇기에 지난주와 마찬가지로 이번 주차의 요구사항 분석/설계도 살짝 어색했다.

이론이야 대학, 교육, 면접준비로 학습했었으나 실무에 적용하지 않으면 무용지물이다.

 

유저 시나리오는 TDD처럼 여러 케이스를 상정하고 개발하고 있다쳐도,
다이어그램은 실서비스에 적용한 적이 없었다.

 

그럼 이번 과제는 어떻게 할까.

 

일단 클로드에게 시켜보자.


기본 요구사항은 '사용자' 입장에서의 '상품', '좋아요', '주문'에 대한 액션 몇 가지로, 꽤나 단순해 보였다.

여기서 '사용자'는 '고객'과 '관리자' 둘로 나눌 수 있다.

 

물론 요구사항에 따라 역할이 부여된 '관리자'가 세부적으로 나뉠 순 있겠으나 그걸 생각하는 건 나중일이다.

액터가 정해졌으면 그들의 여러 시나리오를 작성해야 한다.

 

클로드에게 각각의 시나리오 초안을 작성하게 시키던 중
문득 '왜 굳이 이런 순서로 이것들을 작성해야 할까' 의문이 들었다.

 

하지만 이 의문은 타 팀의 멘토링으로 금세 해결되었다.

 

'왜' 라는 의문으로 시작하면 이런 설계 방식이 자연스러운 흐름이다.

'왜 이걸 만들어야 하는가'

'그럼 사용자는 무엇을 할 수 있어야 하는가'

'사용자 이벤트를 처리하기 위한 시스템 설계는 어떻게 할 것인가'

'실제 구현은 어떻게 할 것인가'

기획자와 개발자 간의 싱크를 맞추는 것부터 시작하여
구현의 구체화 단계까지 가기 위한 과정이며, 당연한 의문이었다.

 

다시 유저 시나리오로 돌아와서.

 

그렇다면 유저 시나리오 단계에서의 비기능적 요구사항들은
어디까지 구체화가 되어야 하는가 생각하게 된다.

 

예를 들어 정렬 조회 시 1초 만에 보여져야 한다와 같은 내용을 적어도 되는 건지,
아니면 대략적으로만 적어도 되는 건지 같이 말이다.

 

유저 시나리오는 요구사항 분석/설계 최종 단계의 구체화를 위한 시작점이다.

시작이 반이라는 말처럼 좋을 수 있지만, 흐름을 알기 위한 것이며 첫 장에 디테일하면 피곤해질 수 있다.

비기능적 요구사항에 대해선 대략적으로만 나타내기로 했다.


멘토님들이 공통적으로 하신 말씀이 있다.

대강 핵심은 시퀀스 다이어그램 작성이 굉장히 중요하다는 것이다.

 

시퀀스 다이어그램은 딱 보고 설명 가능한 정도로 이해가 잘 되도록 구성하는 게 좋다.

이 또한 너무 딥하게 파고들어 구성하면 피로해지고, 이해가 어렵다.

 

불필요하다 싶으면 과감하게 생략하고 중요 생명선과 메시지 전달,
에러 컨트롤 내용을 심플하게 적는 게 핵심인 것 같다.

 

그렇다면 '어느 단위로 생명선을 잡을 것인가'도 고민이 됐다.

 

클래스 단위로 그리면 Service, Facade, Repository까지 전부 등장해서 다이어그램이 길어진다.

 

라이브 세션에서 들은 얘기가 생각났다. 받아 보는 입장에 따라 다르고,
도메인 단위로 묶어서 전체 흐름을 먼저 파악하는 게 협업에서는 더 유용할 때가 많다고 했다.

 

결국 도메인 단위로 묶었다. 클래스 단위 시퀀스는 기술 명세 영역이지,
지금 우리가 작성하는 문서와는 결이 다르다고 판단했다.


다음은 클래스 다이어그램.

 

여기서 첫 번째 선택지가 생겼다. 좋아요 수를 어떻게 관리할 것인가.

 

집계 쿼리로 COUNT(*) 하면 동시성 충돌이 없다. 단순하고 안전하다.

 

반면 Product 테이블에 like_count 필드를 두고 좋아요 이벤트마다 업데이트하면,
좋아요 순 정렬 조회 시 집계 없이 인덱스만으로 빠르게 처리할 수 있다.

 

데이터가 많아지면 단순 집계는 버티기 어렵다.


반면 필드 저장은 동시 좋아요가 몰릴 때 같은 row에 업데이트가 경합한다.

 

결론은 역정규화 후 업데이트로 결정했다.


좋아요 순 정렬이 핵심 기능이고,
현재 규모에서 동시성 이슈는 DB 업데이트로 충분히 커버된다고 봤다.

 

반면 주문 항목 수는 단순 집계로 처리했다.
고객이 고른 상품 개수는 그리 많지 않을 것이고,
이 숫자를 정렬에 쓸 일도 없기 때문이다.

 

중요도에 따라 설계가 달라진다는 걸 이번에 체감했다.


ERD에서는 묵혀뒀던 결정들을 내려야 했다.

 

소프트 딜리트냐 하드 딜리트냐.

 

상품과 브랜드는 소프트 딜리트로 결정했다.

 

재판매 가능성이 있고, 실무에서는 과거 이력 추적이 필요한 경우가 많다.

좋아요는 하드 딜리트. 이력이 불필요하고,

다시 좋아요 이벤트가 발생했을 때의 로직이 단순해진다.

 

결국 FK는 대부분 제거했다.

 

실무에서도 FK를 물리적으로 쓰는 경우는 드물고,
이미 앱 레벨에서 검증이 이루어지고 있기 때문이다.

 

Sam님 멘토링에서도 같은 이야기를 들은 것 같다.


주문을 스냅샷으로 설계한 이유도 정리해둘 필요가 있다.

 

주문 이후 상품 가격이 바뀌거나, 상품명이 수정되거나,
상품이 소프트 딜리트 되는 경우가 실제로 발생한다.

 

이때 주문 기록은 고객이 실제로 구매한 시점의 데이터를 그대로 보여줘야 한다.

 

그래서 OrderItem은 Product를 참조하는 게 아니라,
주문 시점의 productName과 price를 복사해서 고정하는 방식으로 설계했다.

 

Product는 살아있는 카탈로그고, OrderItem은 그 순간을 찍어둔 스냅샷이다.


이름을 통일하지 않은 것도 이 구분을 명확히 하기 위해서다.


이번 주는 요구사항을 받고 '왜 이 순서로 설계하는가'를 이해하는 것부터 시작했다.

 

BRD → PRD → SDD → TSD로 이어지는 흐름이 결국은
'왜 만드는가'에서 '어떻게 만드는가'구체화되는 과정이었다.

'Loopers > WIL' 카테고리의 다른 글

Week 6. 이론과 수치  (0) 2026.06.26
Week 5. 인덱스와 캐시  (0) 2026.06.19
Week 4. 동시성 제어  (0) 2026.06.12
Week 3. 도메인 주도 설계  (0) 2026.05.29
Week 1. 모든 게 처음이었다.  (0) 2026.05.15