[우아한테크코스 7기 FE Lv.2] 16, 17주차 회고

유소정·2025년 6월 10일
3

크루끼리 신나는 밤을 보냈다~
MT 이후로 처음인데 너무 즐겁게 놀았다 😊🎶


폴더 구조에 대해 생각해보기

오늘 아침에 테오의 폴더구조의 변화로 이해하는 프론트엔드 멘탈모델 변천사 글을 읽고, 그동안 은은하게 느껴왔던 폴더 구조의 불편함과 그 이유가 조금 명확해졌다.

지금까지는 역할 중심의 구조(components, hooks, services...)로 프로젝트를 구성해왔는데, 미션이 점점 복잡해지고 커지면서 다음과 같은 문제를 마주하게 됐다.

👀 현재 구조

/src
├── api                  # API 호출
├── assets               # 이미지 등 정적 리소스
├── components
│   ├── common           # 공통 UI 컴포넌트
│   ├── layout           # 레이아웃 관련 컴포넌트
│   └── shoppingCart     # 장바구니 도메인 컴포넌트
├── contexts             # Context API 상태관리 훅
├── hooks                # 커스텀 훅
├── pages                # 전체 페이지
├── router.tsx           # 경로 설정 파일
├── types                # 타입 선언
└── utils                # 유틸리티 함수

⚠️ 문제점

1. 공통 UI 컴포넌트가 도메인에 의존하고 있다.

시간이 지나며 여러 컴포넌트에서 사용된다는 이유로 common 폴더에 도메인 로직이 들어오기 시작했다.
이름은 '공통'이지만 실제로는 특정 도메인에 종속적인 컴포넌트들이 섞여 있는 상황이 되었다.

2. 도메인을 충분히 세분화하지 않았다.

현재는 shoppingCart만 도메인 폴더로 분리되어 있고,
사실상 order, product 등으로도 나눠야 하는 도메인 로직이
모두 shoppingCart 폴더 안에 뭉뚱그려져 있다.
도메인 간 책임이 섞이면서 구조적으로 확장성과 가독성이 떨어지는 상황이었다.

3. router.tsx가 최상위에 위치한다.

router.tsx 같은 설정 파일이 상위에 놓이면서,
프로젝트가 커질수록 파일들이 얕고 넓게 퍼질 위험이 있었다.

🔧 개선 방안

폴더 구조를 역할 기반 → 도메인 중심으로 확장한다면?

  1. 공통 UI 컴포넌트는 도메인 의존성을 제거한다.
    → components/ui에는 도메인과 무관한 순수 UI 요소만 위치시킨다.
    (이때 아토믹 디자인 패턴을 이용해서 계층을 구분해봐도 좋을 것 같다.)

  2. hooks, services, store 등도 도메인 단위로 분리한다
    → 관련 파일을 한 폴더 안에 모아 응집도를 높이고 탐색을 단순화한다.

  3. router.tsx 등 설정 파일도 관련 구조 하위로 재배치한다
    → 페이지 중심 구조(pages 또는 app) 하위에서 경로를 관리한다.

📁 예상 구조

/src
  /pages (또는 /app)     # 라우팅 진입점
    index.tsx
    about.tsx
    /products
      index.tsx
      [id].tsx
    /cart
      index.tsx
      checkout.tsx
    /auth
      login.tsx
      register.tsx
      
  /components           # UI 컴포넌트
    /ui                 # 순수 UI (도메인 무관)
      Button.tsx
      Modal.tsx
      Card.tsx
      Input.tsx
    /product           # 제품 도메인
      ProductCard.tsx
      ProductList.tsx
    /cart              # 장바구니 도메인
      CartItem.tsx
      CartSummary.tsx
    /layout            # 레이아웃 컴포넌트
      Header.tsx
      Footer.tsx
      Sidebar.tsx
      
  /hooks               # 커스텀 훅
    /ui                
      useModal.ts
      useDebounce.ts
    /product          
      useProducts.ts
      useProductDetail.ts
    /cart             
      useCart.ts
    /auth
      useAuth.ts
      usePermissions.ts
      
  /services           # API 레이어
    /api              
      client.ts
      interceptors.ts
    /product          
      productApi.ts
    /cart             
      cartApi.ts
    /auth
      authApi.ts
      
  /store              # 상태 관리
    /slices
      productSlice.ts
      cartSlice.ts
      authSlice.ts
    store.ts
    
  /utils              # 유틸리티
    /common
      formatters.ts
      validators.ts
      constants.ts
    /product
      priceCalculator.ts
    /date
      dateHelpers.ts
      
  /types              # TypeScript 타입 정의
    /models
      product.ts
      user.ts
      cart.ts
    /api
      responses.ts
      requests.ts
      
  /styles             # 스타일 파일
    /globals
      reset.css
      variables.css
    /components
      button.module.css
      
  /assets             # 정적 리소스
    /images
    /fonts
    /icons

☕️ 마무리하며

헤먼드가 잭슨의 PR에서 남긴 피드백이 인상 깊었다,

일단 응집도에 대한 답변에 이어서 제가 또 질문을 드려보자면,
응집도가 높은 상태가 이전 단계의 미션처럼 "하나의 모듈로 만들어서 배포한다" 고 생각해보시면 어떨까요!?
말씀해주신 product, cart, error 를 각각의 모듈로 만들어서 배포하기 쉬운 구조일까요?
지금은 모든 내용이 다 파편화 되어있어요.
product에 대한 내용을 추가하거나 수정하고 싶으면 거의 모든 폴더를 들춰봐야 알 수 있죠.
이걸 응집도가 높은 상태라고 하긴 어려울 것 같아요.

현재 내가 개선한 폴더 구조는 역할별 구조에서 도메인별 구조로의 확장이지만,
아직도 아래처럼 여러 폴더를 오가며 작업해야 하는 점은 완전히 해결되지 않았다.

/components/cart/   # 장바구니 UI 컴포넌트
/hooks/cart/        # 장바구니 로직 훅
/services/cart/     # 장바구니 API 호출
/store/cart/        # 장바구니 상태 관리

즉, 도메인별로 잘 나뉘어 있긴 하지만 관련 로직이 분산되어 있어
실제로는 하나의 변경을 위해 여러 위치를 탐색해야 하는 번거로움이 여전히 존재한다.
게다가 이 구조에서는 product, cart 같은 도메인을 하나의 모듈로 묶어 배포하기도 쉽지 않다.

그래서 더 나아가 도메인 중심 → 역할 내부 정리가 아니라
도메인 자체를 루트로 올리는 구조도 고려해볼 수 있을 것 같다.
이런 방식은 아래처럼 구성된다.

/src
  /Product             # 제품 도메인
    /components        # 제품 관련 컴포넌트
    /hooks             # 제품 관련 훅
    /services          # 제품 관련 API
    /utils             # 제품 관련 유틸리티

  /Cart                # 장바구니 도메인
    /components        # 장바구니 관련 컴포넌트
    /hooks             # 장바구니 관련 훅
    /services          # 장바구니 관련 API
    /store             # 장바구니 관련 상태

  /User                # 사용자 도메인
    /components
    /hooks
    /services
    /store

  /shared              # 공통 코드
    /components        # 공통 UI 컴포넌트
    /hooks             # 공통 훅
    /utils             # 공통 유틸리티

이렇게 구성하면, product 도메인 하나에 대한 관련 책임이 아래로 모이게 된다.
이 구조를 DDD(도메인 주도 설계)라고 하나?

물론, 아직까지는 내 프로젝트에서 위와 같은 구조가 인지 부하를 일으킬 정도로 크진 않다.
하지만 도메인이 늘어나고 팀원이 많아지는 상황이라면 하나의 도메인을 하나의 모듈처럼 관리하고 배포할 수 있는 구조를 선택할 수 있을 것 같다.

더 나아가 현재처럼 구현하지 않고
'행동'을 기반으로 기능 응집 구조를 하는 방법도 있는 것 같은데,
이것도 아직까지 이 구조가 인지부화를 일으키지 않아서 다음에 필요할 때 시도해봐야겠다.

이렇게 정리하고 보니, 어떻게 하면 관련된 것을 잘 모아서 책임을 모아놓고 변화가 생길 때 인지부화없이 최소한으로 작업할 수 있을까를 좀 고민하게 된 것 같다.

결국 폴더 구조는 나의 멘탈 모델이기 때문에, 뭔가의 기준으로 응집되어 있을 때 이해하기 쉬워지는 것 같다.

호감이 가는 사람의 특징

우테코에 온 지 벌써 119일 🎉
기념으로 써보는 호감 가는 사람의 특징 호호

  • 웃긴 사람
  • 솔직하게 말하는 사람
  • 무례하지 않으려고 노력하는 사람
  • 자신만의 생각이 있는 사람
  • 자신감 있는 사람
  • 자신만의 개성이 보여지는 사람

나도 이런 사람이 되야지 ~ (◕‿◕)

인터페이스가 중요한 이유

레벨 1부터 interface를 '계약서'처럼, 클래스의 형태를 정의하는 용도로 사용해왔다.
이런 관점이 이어져서, 지금은 다음과 같은 기준에 따라 interface와 type을 구분해서 사용하고 있다.

  • 컴포넌트의 props는 외부에서 주입받는 명시적인 계약이라고 생각해서 interface를 사용
  • Product처럼 백엔드에서 전달받는 데이터 객체는 조합하거나 가공할 일이 많아서 type으로 정의
    → 유니온(|)이나 인터섹션(&) 같은 타입 연산자를 활용하기 편하기 때문

그런데 수업을 들으면서 자연스럽게 interface를 어떻게 구현할지가 중요한 주제로 다뤄졌고,
"인터페이스를 어떻게 작성할까?"에 대해 더 고민하게 됐다.

이때 등장한 두 가지 접근 방식.

  • 구현 중심 인터페이스
    • 내가 평소에 주로 하던 방식이다. 먼저 구현을 만들고, 거기에 맞춰서 interface를 작성하는 방식. 실제 코드가 존재하니까 작성하기는 편하지만, 책임보다 구현에 끌려가는 느낌이 있다.
  • 책임 중심 인터페이스
    • 수업 시간에 강조된 방식이다. 구현에 앞서 인터페이스부터 먼저 구성해본다. 확실히 책임 중심으로 생각하면, 테스트 코드를 짤 때처럼 기능의 책임이 어디까지인지 먼저 정의해볼 수 있어서 좋다. 외부로 책임이 흐르지 않도록, 컴포넌트가 정확히 어떤 역할을 가져야 할지를 스스로 묻게 된다.

마지막 수업 QnA 시간에 쫌쫌다리한 이야기

  • 대 AI 시대에 어떻게 개발하면 좋을까
    • 설계가 중요하다. 무작정 코딩을 시작하기보다는, 설계를 먼저 해야 한다. 유지보수 관점에서도 그렇고, 프로그래밍 자체는 이제 AI가 더 잘하는 시대이기 때문에 더더욱 그렇다.
    • AI 시대에는, 지식은 어떻게든 배울 수 있다. 그래서 진짜 중요한 건 효율적으로 배우는 능력이다. 현업을 하다 보면, 새로운 기술을 배워야 할 순간이 정말 많다. 학습 방법을 한 번 효율화해두면, 앞으로 배울 모든 지식을 훨씬 더 빠르게 흡수할 수 있다.
  • 응집도가 높다는 건, 하나가 바뀌면 다 바뀔 정도여야 한다는 뜻이다.
  • 구조를 많이 고민하면서, "내 코드에서 냄새가 나는 부분이 어딜까?", "이건 왜 불편하게 느껴졌을까?"를 자꾸 돌아보게 돼야 한다. 결국 중요한 건, 자기 코드의 스멜을 맡을 수 있는 감각, 그리고 그걸 말로 표현할 수 있는 능력이다.
  • 토스에서는 시간이 없는데 구현해야 하는 게 있으면, 완전 고립되게 짠다고 한다. 다른 거에 영향이 안 미치게 하고, 나중에 고립된 부분만 다시 짠다고 한다.
  • 지식을 넓히는 게 꼭 제일 중요한 건 아니다. 중요한 건 내가 어떤 문제를 마주쳤고, 왜 그게 풀어야 하는 문제였고, 나는 어떤 사고로 해결 방법을 선택했는지, 다른 방식과 비교했을 때 어떤 생각을 했는지. 이걸 통해 "아, 이 사람은 문제를 잘 풀어가는 사람이구나. 다른 걸 학습시켜도 잘 하겠구나."라는 인상을 주는 게 더 중요하다고 생각한다. 많이 아는 것보다 근본적인 사고 역량!
  • 방학 때 의무감으로 공부하지말고 그냥 자신이 하고 싶은 거 공부해라!
  • 막연하게 'JavaScript 공부해야지!'라고 생각하면, 끝이 없는 동굴 속에 있는 기분이다. 그래서 '내가 해결하고 싶은 문제'를 먼저 잡고, 그 문제를 해결하는 데 필요한 JavaScript, TypeScript, React, 네트워크, 알고리즘 등을 엮어서 공부하면 더 좋지 않을까?
    1. 일단 구현하기 2. 성능 챙기기

레벨 3를 앞두며

1. 내가 팀에 어떤 부분을 지속적으로 기여할 수 있지?

레벨 3 팀 프로젝트는 실무는 크게 다르지 않다고 한다.
그래서 팀에서 내가 지속적으로 기여할 수 있는 (내가 좋아하는) 방법을 찾아볼 수 있는 기회라는 생각이 든다. 예를 들어, 라이브러리 코드를 뜯어보는 것이 될 수 있다.

2. 이끌거나 따르거나 아니면 비켜서라

나는 지금까지 주로 리더 역할을 맡아왔다. 동아리 회장, 졸업작품 팀장, 해커톤에서도 팀장을 맡았고, 성실한 이미지 때문인지 자연스럽게 앞에 서게 되었다.

효율적인 협업을 위해 스크럼, 피그잼, 뽀모도로, 코어타임 등 다양한 방식을 도입해봤다. 팀원들이 부담 없이 의견을 나눌 수 있도록 협업 구조를 계속 개선해왔다. 이런 경험은 나를 자연스럽게 '사람'과 '팀'에 대한 고민으로 이끌었다. 『비폭력 대화』, 『함께 자라기』, 『상자 밖의 사람들』 같은 책들을 읽고, 작게나마 팀 내 대화 방식에 적용해보기도 했다.

그런데 아이러니하게도, 그 모든 과정을 겪으며 느낀 건 "나는 리더보다 팔로워가 더 잘 맞는다"는 사실이었다. 가장 큰 이유는, 모든 걸 혼자 짊어지기엔 벅찼기 때문이다. 서브 리더가 있었으면 좋았겠지만, 대부분 "개발에만 집중하고 싶다"는 입장이었고, 그만큼 팀장의 고충을 나누기 어려운 환경이기도 했다.

팀장으로서 나는 기술과 소프트스킬 모두를 혼자 먼저 실험해보고, 괜찮은 방법을 팀에 제안하고 도입하는 역할을 맡았다. 그래서 개인 개발 이외에 일로 더 바쁘기도 했다.

그래서 이제는 팔로워가 되려 한다. 늘 앞에서 방향을 잡고, 분위기를 조율하고, 문제를 해결하던 역할에서 한 발 물러나, 좋은 팀장을 지지하고, 더 나은 팀워크를 만들어가는 사람이 되고 싶다.

팔로워로 있을 때는 리더가 겪는 부담과 맥락을 이해하고,
주도적으로 협력하는 방법을 익힐 수 있다고 생각한다.

🍵 마무리

레벨 2가 얼마 남지 않았다.
마지막 미션은 정말 정신없고, 체력적으로도 꽤 힘들었다.
특히 테스트는 어떻게 짜야 할지 도무지 감이 안 와서 머리가 어지러울 정도였다.

그래도... 곧 끝이 난다.
마지막까지 리뷰 잘 반영하면서,
끝까지 화이팅팅 ~ !

profile
기술을 위한 기술이 되지 않도록!

0개의 댓글