sayyoon.site
postsguestbook
🌿
SKALABEFE

SW 미니프로젝트 - 서비스 기획, 요구사항 분석, 시스템 설계, 데이터 모델링

2025.04.26

sw 미니프로젝트로 스스로 하나의 서비스를 기획하는 프로젝트를 진행했다. (프로젝트 개발X 기획O)

 

프로젝트 기획 과정

프로젝트 기획은 [서비스 배경 및 목적] → [사용자 별 시스템 기능(요구사항) 정의] → [서비스 UI 흐름 (와이어프레임 작성)] → [데이터 모델링 설계] → [API 명세 작성] 단계로 진행했다.
촉박한 기간으로 개발까지 진행하지는 않았지만, 직접 개발할 것을 고려해서 서비스를 기획했다.
(애정이 듬뿍 담겼다는 말!!)

그래서 서비스 규모가 크지는 않고, 2일 동안 화면 디자인, erd 설계, api 명세를 작성할 수 있을 만큼으로 서비스 구조를 잡았다.
내가 기획한 서비스는 늘의 뷰 서비스, 줄여서 오소리 서비스다.

개인적으로 서비스를 기획할 때 사람들 눈에 톡 띄는 서비스 이름이 중요하다고 생각하는 사람이라, 프로젝트 기획 단계에서 서비스 이름이랑 서비스 컨셉에 대해서도 고민을 꽤나 했다. (발표 할 때 시간관계상 서비스 기획 내용을 더 말하지 못했어서 블로그에 작성해본다 ㅎㅎ)
서비스의 캐릭터를 오소리로 정하면서 이름도 ‘오소리’로 통일했고, 색상까지 세심하게 설정했다. 발표 후 팀원이 이름이 귀엽다고 해줘, 이름까지 신경 쓴 걸 알아준 것 같아 정말 뿌듯했다.

그 뒤, 기획배경과 목적, 기대효과를 다듬고, 사용자층도 특정한 뒤, 화면 디자인을 진행했다.
오랜만에 사용하는 피그마라 처음에는 재밌었는데, 역시나 레이아웃을 제대로 활용하지 못하다 보니 중간 쯤부터 힘들었다..  

ui

 

그 뒤, 화면 UI를 참고해서 ERD를 정비했다. 확실히 디자인을 하면서 초기 기획했던 데이터 중 필요 없던 것도 있었고, 추가된 것도 있었기 때문에 이 순서대로 진행해서 작업의 중복성을 줄일 수 있었다. ERD는 총 5개의 테이블이 나왔다! (사실 처음에 테이블이 4개밖에 안 나와서 초기 기획에 기능을 더 추가했다😓)

API는 총 11개가 나왔고, auth, spending, summary, user로 기능을 나누어 분리했다.

 

소감

학교 다닐 때는 대부분 팀을 꾸려 기획도 같이 진행하고 UI 디자인은 디자인과 학생이 해주는 경우가 많았어서, 이렇게 온전히 체계적으로 기획한 건 정말 오랜만이었다. 기획을 하다 보니 서비스에 애정이 생겨서 여기서 배운 기술스택으로 개발하고 싶다는 욕심도 생겼지만,, 정말 너무 시간이 없다.. ㅠ (핑계처럼 들릴 수도 있지만 벌써 4월 말..) 앞으로 남은 프로젝트들 열심히 하면서 배우는 내용 습득해야지..! 화이팅
힐링 그 자체였던 SW 미니프로젝트 시간.. 안녕👋 즐거웠어

 

피드백

교수님 피드백으로 알게 된 JWT 사용자 식별의 진짜 의미

이번에 교수님께 받은 피드백 덕분에 내가 놓치고 있었던 중요한 개념을 깨닫게 됐다.

API 호출 시 토큰을 전달하더라도, 개인 데이터 조회나 계정 관리 API에서는
 반드시 사용자 식별 정보(user_id 등)를 명확히 식별해 처리해야 한다

나는 지금까지 OpenAPI 명세에 bearerAuth를 선언했고, 프론트엔드에서는 API 요청 시 Authorization: Bearer 헤더를 잘 붙여서 요청하고 있었기 때문에 잘 만들었다고 생각했다.

하지만 사실 내가 명시한 건 단순히 “이 API는 JWT가 필요하다” 라는 보안 요구사항 선언일 뿐, 실제 사용자 식별과 데이터 필터링은 빠진 상태였다는 걸 깨달았다.

진짜 중요한 건 “사용자 식별 로직”

단순히 토큰이 있다고 끝이 아니라, 서버에서는 그 토큰의 내용을 해석해서 어떤 사용자인지 식별해야 진짜 보안이 완성된다.

즉, 모든 사용자가 /summary/monthly 를 호출했을 때 같은 데이터를 보여주면 안 되고, 각 사용자에게 맞는 데이터만 응답해야 한다.

사용자 식별 방법

  1. JWT Claims 기반 식별 (권장)
    JWT 토큰 안에는 보통 다음과 같은 Claims 정보가 들어간다.

    {
    "user_id": "123",
    "email": "user@example.com",
    "exp": 1715880000
    }

    서버는 이 토큰을 디코딩해서 user_id 를 추출하고, 해당 사용자의 데이터만 조회해 반환해야 한다.

    ‘토큰이 있냐 없냐’ 보다 ‘토큰 안에 누가 있냐’ 가 더 중요하다는 것.

  2. Path/Query 파라미터 기반 식별 (비추천)
    경로나 쿼리에 사용자 ID를 넣는 방식도 있다.

    GET /users/123/summary

    하지만 이 방법은 토큰 없이 접근 가능하게 만들 위험이 크다. 따라서 토큰 검증 + Claims 기반 식별이 더 안전하고 권장되는 방식이다.

예시 흐름

사용자가 요청할 때 이렇게 해야 한다.

  1. 사용자 요청

    GET /summary/monthly?year_month=2024-05
     Authorization: Bearer eyJhbGciOi... (user_id=123 포함)
  2. 서버가 토큰 디코딩

    decoded = decode_jwt(token)
     user_id = decoded["user_id"]
  3. 해당 사용자 데이터만 조회 후 응답

    {
        "result": 0,
        "body": { ... user 123's data ... }
     }

OpenAPI 문서 개선 포인트

  1. 보안 공통 선언으로 정리
    개별 API에 일일이 bearerAuth를 붙이는 것보다 공통 선언으로 빼면 유지보수가 훨씬 좋다.

    security:
    - bearerAuth: []
  2. JWT Claims 구조 명세 추가
    문서 독자가 JWT 안에 무슨 정보가 필요한지 모르면 구현이 불가능하니, Claims Example을 문서에 명확히 보여줘야 한다.

    components:
    securitySchemes:
        bearerAuth:
        type: http
        scheme: bearer
        bearerFormat: JWT
        description: |
            JWT Claims Example:
            {
            "user_id": "string (required)",
            "email": "string (optional)",
            "exp": "timestamp"
            }

개선된 OpenAPI 예시

공통 보안 선언 + Claims 설명 + 에러 응답 정의까지 정리한 버전

openapi: 3.0.3
info:
  title: 소비 기록 API
  version: 1.0.0

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        JWT Claims Example:
        {
          "user_id": "string (required)",
          "email": "string (optional)",
          "exp": "timestamp"
        }

  schemas:
    ErrorResponse:
      type: object
      properties:
        code:
          type: integer
        message:
          type: string
        result:
          type: integer

security:
  - bearerAuth: []  # ✅ 모든 API에 공통 적용

paths:
  /spending:
    post:
      tags:
        - spending
      summary: 소비 기록 등록
      description: 하루 소비 기록을 작성합니다.
      ...
      responses:
        '200':
          description: 소비 기록 등록 성공
        '401':
          description: 인증 실패 (유효하지 않은 토큰)
        '403':
          description: 권한 없음 (잘못된 사용자 접근)

  /summary/monthly:
    get:
      tags:
        - summary
      summary: 월별 소비 요약 통계 조회
      description: 사용자별 월별 소비 요약을 조회합니다.
      ...
      responses:
        '200':
          description: 월별 소비 요약 조회 성공
        '401':
          description: 인증 실패 (유효하지 않은 토큰)
        '403':
          description: 권한 없음 (잘못된 사용자 접근)

이번 피드백을 통해 확실히 배운 것

보안 요구사항 명시만이 아니라 JWT Claims 기반 사용자 식별 로직 구현이 진짜 중요하다. 그리고 이걸 문서로도 명확히 설명해줘야 한다. 이번 피드백 덕분에 또 하나를 알게된 것 같다. 그저 빛, 교수님,,⭐️

© Powered by danmin