프로젝트 목록으로 돌아가기
Recruiter Briefmemory-of-year

Memory of Year

7인 팀 프로젝트에서 백엔드 리드로 API·배포 기준을 잡고, 편지 목록 N+1 비교용 k6 시나리오를 정리했습니다.

Role
백엔드 리드
Period
2024
Team
7인 팀
Theme
Spring/JPA
Java 17Spring Boot 3Spring SecurityJWTSpring Data JPAMySQLH2AWS S3

30초 요약

문제
앨범·편지 목록에서 사진 개수 조회와 API 기준 정리가 팀 협업 병목이 될 수 있었습니다.
해결
서브쿼리 COUNT, API 공통 규칙, S3 업로드 흐름, k6 목표 기준을 정리했습니다.
검증
편지 목록과 앨범 조회는 실제 측정값이 아니라 p95 목표 기준으로 분리해 남겼습니다.

Problem

Problem

7인 팀 프로젝트에서 백엔드 리드로 API 기준을 맞추면서, 앨범과 편지 목록처럼 화면에 자주 노출되는 조회 API의 N+1 위험을 다뤘습니다.

Decision

Decision

팀원이 기능을 확장하기 전에 DTO, API 구조, 로컬 실행 기준을 정리하고, 조회 성능은 k6 스크립트로 재현 가능한 형태를 남겼습니다.

Build

Implementation

  • 편지 목록은 사진 개수를 함께 보여주는 요구를 고려해 N+1 비교 모드가 있는 k6 스크립트로 확인 경로를 만들었습니다.
  • 앨범 조회는 owner와 letters 접근을 함께 고려해 EntityGraph 기반 조회 최적화를 적용했습니다.
  • Docker Compose와 CI 기준을 맞춰 팀원별 로컬 MySQL 설정 차이에서 생기는 불일치를 줄였습니다.

팀 리드 역할은 직접 작성한 코드뿐 아니라 팀원이 같은 기준으로 API와 성능을 확인할 수 있는 재현 경로를 만드는 일이라는 점을 정리했습니다.

Proof

Verification

편지 목록 k6와 앨범 조회 k6 스크립트에서 fixture, VU, p95 목표 기준을 확인할 수 있게 남겼고, 장기 운영 지표는 별도 한계로 분리했습니다.

30개
Verified
편지 목록 쿼리

편지 30개 fixture로 목록 조회를 재현

20 VU
Target
편지 목록 부하

p95 <500ms 목표 기준과 실패율 기준 설정

30 VU
Target
앨범 API

앨범 조회 p95 <200ms 목표 기준 시나리오

Target

30개

편지 목록

Scenario
앨범의 편지 목록과 사진 개수 조회
Method
편지 30개 fixture와 N+1 비교 모드가 있는 k6 스크립트로 검증
Result
편지 목록 조회의 p95 목표 기준과 실패율 기준을 k6에 명시했습니다.
Target

30 VU

앨범 API

Scenario
앨범 조회 부하 테스트
Method
k6 스크립트로 30 VU, 30초 부하와 p95 목표 기준을 설정
Result
앨범 조회 성공 응답과 p95 <200ms 목표 기준을 확인하는 경로를 남겼습니다.

Boundaries

Trade-offs & Limitations

Trade-offs

  • 서브쿼리 COUNT는 목록 화면에 필요한 값을 한 번에 가져오지만 복잡한 필터가 늘면 쿼리 가독성이 떨어질 수 있습니다.
  • 백엔드 공통 규칙을 먼저 정하면 협업 속도는 좋아지지만 초기 설계 시간이 필요합니다.

Limitations

  • 팀 프로젝트 기간 내 핵심 API와 성능 개선에 집중해 장기 운영 지표는 없습니다.
  • S3 업로드는 기본 업로드 흐름 중심으로 검증했습니다.
프로젝트 다이어그램

아키텍처

전체 아키텍처

architecture

전체 아키텍처

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Application

API, 도메인 로직, 워커 처리

프론트엔드

Client

REST API

Backend

JWT 검증

Backend

Data / Messaging

상태 저장, 캐시, 이벤트 전달

MySQL

Data

S3

Data

핵심 연결 흐름
  1. 1. REST API → JWT 검증
  2. 2. 프론트엔드 → REST API
  3. 3. REST API → MySQL
  4. 4. REST API → S3
원본 Mermaid 보기
flowchart LR
    subgraph Client
      FE[프론트엔드]
    end
    subgraph Backend["Spring Boot"]
      API[REST API]
      JWT[JWT 검증]
      API --> JWT
    end
    subgraph Data
      MySQL[(MySQL)]
      S3[(S3)]
    end
    FE --> API
    API --> MySQL
    API --> S3

ERD

전체 ERD

erd

전체 ERD

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

users
bigint
user_id
PK
varchar
username
UK
varchar
nickname
UK
varchar
email
UK
varchar
role
album
bigint
album_id
PK
bigint
user_id
FK
varchar
title
varchar
album_color
boolean
visibility
varchar
sticker_url
letter
bigint
letter_id
PK
bigint
album_id
FK
varchar
letter_title
text
content
datetime
created_at
boolean
is_anonymous

+ 1개 필드

photo
bigint
photo_id
PK
bigint
letter_id
FK
varchar
url
varchar
comment
varchar
sticker_url
관계 요약
  1. #1 users ||--o{ album · 소유
  2. #2 album ||--o{ letter · 포함
  3. #3 letter ||--o{ photo · 포함
원본 Mermaid 보기
erDiagram
    users ||--o{ album : "소유"
    album ||--o{ letter : "포함"
    letter ||--o{ photo : "포함"

    users {
      bigint user_id PK
      varchar username UK
      varchar nickname UK
      varchar email UK
      varchar role
    }
    album {
      bigint album_id PK
      bigint user_id FK
      varchar title
      varchar album_color
      boolean visibility
      varchar sticker_url
    }
    letter {
      bigint letter_id PK
      bigint album_id FK
      varchar letter_title
      text content
      datetime created_at
      boolean is_anonymous
      varchar letter_color
    }
    photo {
      bigint photo_id PK
      bigint letter_id FK
      varchar url
      varchar comment
      varchar sticker_url
    }

시퀀스 다이어그램

로그인

sequence

로그인

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Participants
CAPIUSURJWT
  1. 1요청

    C → API

    POST /api/auth/login

  2. 2검증

    API → US

    authenticateUser

  3. 3저장

    US → UR

    findByUsername

  4. 4alt 성공

    control

    alt 성공

    조건: alt 성공

  5. 5검증

    API → JWT

    createToken

    조건: alt 성공

  6. 6검증

    API → C

    200 { token }

    조건: alt 성공

  7. 7else 실패

    control

    else 실패

    조건: else 실패

  8. 8else 실패

    API → C

    401

    조건: else 실패

전체 메시지 상세
StepFrom → ToMessageCondition
1C → APIPOST /api/auth/login-
2API → USauthenticateUser-
3US → URfindByUsername-
4controlalt 성공alt 성공
5API → JWTcreateTokenalt 성공
6API → C200 { token }alt 성공
7controlelse 실패else 실패
8API → C401else 실패
원본 Mermaid 보기
sequenceDiagram
    C->>API: POST /api/auth/login
    API->>US: authenticateUser
    US->>UR: findByUsername
    alt 성공
        API->>JWT: createToken
        API-->>C: 200 { token }
    else 실패
        API-->>C: 401
    end
앨범 생성

sequence

앨범 생성

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Participants
CAPIASAR
  1. 1요청

    C → API

    POST /api/albums/create

  2. 2처리

    API → AS

    hasAlbum, createAlbum

  3. 3저장

    AS → AR

    save

  4. 4처리

    API → C

    201 { Album }

전체 메시지 상세
StepFrom → ToMessageCondition
1C → APIPOST /api/albums/create-
2API → AShasAlbum, createAlbum-
3AS → ARsave-
4API → C201 { Album }-
원본 Mermaid 보기
sequenceDiagram
    C->>API: POST /api/albums/create
    API->>AS: hasAlbum, createAlbum
    AS->>AR: save
    API-->>C: 201 { Album }
편지 작성

sequence

편지 작성

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Participants
CAPILSLR
  1. 1요청

    C → API

    POST /api/albums/{id}/create

  2. 2처리

    API → LS

    createLetter

  3. 3저장

    LS → LR

    save

  4. 4처리

    API → C

    201 { Letter }

전체 메시지 상세
StepFrom → ToMessageCondition
1C → APIPOST /api/albums/{id}/create-
2API → LScreateLetter-
3LS → LRsave-
4API → C201 { Letter }-
원본 Mermaid 보기
sequenceDiagram
    C->>API: POST /api/albums/{id}/create
    API->>LS: createLetter
    LS->>LR: save
    API-->>C: 201 { Letter }
사진 업로드

sequence

사진 업로드

Mermaid 원본을 파싱해 텍스트는 DOM으로 렌더링합니다.

Participants
CAPIPSS3PR
  1. 1요청

    C → API

    POST /api/letters/{id}/photos

  2. 2처리

    API → PS

    addPhoto

  3. 3처리

    PS → S3

    uploadFile

  4. 4저장

    PS → PR

    save

  5. 5처리

    API → C

    201 { Photo }

전체 메시지 상세
StepFrom → ToMessageCondition
1C → APIPOST /api/letters/{id}/photos-
2API → PSaddPhoto-
3PS → S3uploadFile-
4PS → PRsave-
5API → C201 { Photo }-
원본 Mermaid 보기
sequenceDiagram
    C->>API: POST /api/letters/{id}/photos
    API->>PS: addPhoto
    PS->>S3: uploadFile
    PS->>PR: save
    API-->>C: 201 { Photo }