[개발 블로그 제작기] 3. 서버 API 문서 작성

김현우·2021년 9월 13일

이전 포스트에서 화면별로 어떤 데이터를 어느 시점에 서버로부터 받아올 지 생각했습니다. 그걸 토대로 API 문서를 작성해 봅시다.

API 문서에는 무엇을 적을까요

작성자나 사내 컨벤션에 따라 조금씩 다르겠지만 거의 모든 API문서가 공통으로 적는 항목들이 있습니다.

  • Interface ID: 여러 API 항목들을 구별하는 ID
  • Endpoint: 요청 경로 + HTTP 메서드
  • Description: 간단한 설명
  • Request Payload: 클라이언트가 요청 시 보내는 데이터
  • Response Data: 서버가 응답으로 보내는 데이터

저는 저 5가지만 적도록 하겠습니다.

원래는 Request Payload와 Response Data 항목에 각 데이터의 타입, 필수여부, 설명, 비고 등 적을 게 더 많습니다.
근데 저는 지금 Markdown의 table 문법을 이용해서 써내려가야 하니 그런 복잡한 표는 만들기 힘드네요..! 이번엔 이름과 타입만 적어주고 넘어가겠습니다.

Type Define Sheet

보통 API 문서에는 Response Data나 Request Payload에 들어가는 데이터들의 타입을 명시하기 위해 시트를 따로 둡니다.
타입을 따로 명시함으로써 문서 본문을 좀 더 효율적으로 작성할 수 있습니다. 문서에 나올 데이터 타입들을 먼저 적어보겠습니다.

NameDescriptionItem
TContentPreview컨텐츠 요약 정보- idx: number
- title: string
- category: string
- thumbnail: string
- createdAt: string
TContent컨텐츠 상세 정보- idx: number
- title: string
- category: string
- createdAt: string
- content: string
TCntContents총 컨텐츠 수,
카테고리별 컨텐츠 수 집계
- total: number
- perCategory: [string, number][]
TCntLike총 따봉 수,
카테고리별 따봉 수 집계
- total: number
- perCategory: [string, number][]
TCategoryDetail카테고리 상세 정보- name: string
- idx: number
- priority: number (= 순서)
TVisitor각종 방문자 수- total: number
- today: number
- months: [string, number][] (= 순서)

API 문서 본문 작성

이제 한 화면씩 필요한 API를 작성합니다

1. 메인화면

IDEndpointDescriptionRequest PayloadResponse Data
MAIN_1GET /main방문자 수와 HOT 컨텐츠-- cntToday: number
- cntTotal: number
- contentPreviewsByHit: TContentPreview[]
- contentPreviewsByLike: TContentPreview[]
CATEGORY_1GET /categories카테고리 이름 목록-- categories: string[]

2. 컨텐츠 목록 화면

IDEndpointDescriptionRequest PayloadResponse Data
CONTENT_1GET /contents특정 카테고리에 해당하는 컨텐츠 목록 (요약 정보)- category: string
- offset: string
- limit: string
- contentPreviews: TContentPreview[]

+ CATEGORY_1 (카테고리 이름 목록)

중복 내용은 적지 않고 위처럼 +로 표기 하겠습니다

Request Payload의 offset과 limit는 무한 스크롤이나 페이징을 구현할 때 불가피한 데이터입니다. 서버로에게 "n번째 데이터부터 m개 줘" 라고 해야 하니까 n과 m을 의미하는 데이터를 보내야 하며, offset=n, limit=m입니다.

3. 컨텐츠 상세 화면

IDEndpointDescriptionRequest PayloadResponse Data
CONTENT_2GET /content특정 컨텐츠 상세 정보와 이전/다음 컨텐츠 링크- cid: string- contentData: TContent
- prevContentPreview: TContentPreview
- nextContentPreview: TContentPreview
CONTENT_3POST /like특정 게시물에 따봉- cid: number- code: number
- msg: string

+ CATEGORY_1 (카테고리 이름 목록)

CONTENT_2와 CONTENT_3의 Request Payload를 보면 똑같이 cid를 실어 보내는데 타입이 다릅니다.
GET 요청을 보내면 Request Payload들이 URL에 String형태로 따라 붙습니다. (예시. /content?cid=12)
number로 넣어봤자 string으로 보내질테니 GET 요청에서는 string으로 썼습니다.

4. (관리자) 로그인 화면

IDEndpointDescriptionRequest PayloadResponse Data
MASTER_1POST /adm/auth관리자 인증 (로그인)- k: string- token: string OR null
- code: number

Request Payload의 k
관리자 패스워드입니다.
보통 로그인이라 하면 id, password를 입력하는데
여러 사용자 중 password가 겹치는 사용자가 있을 수 있습니다.
즉, 회원 테이블 내에서 password는 테이블의 row(회원)을 특정하는 컬럼이 될 수 없습니다.
id로 회원을 특정하고 그 회원의 패스워드와 입력 받은 패스워드를 비교하여 사용자를 인증합니다.
ID는 말그대로 Identifier(식별자)입니다. 지금 로그인하려는 이 사람이 뭐하는 사용자인지 식별하기 위한 값입니다.
그래서 제 관리자 사이트에는 아이디가 필요 없습니다. 로그인 대상은 애초에 저 한명이거든요.
그래서 Identifier없이 패스워드만 입력하고자 하며, 이름은 password말고 key라고 부르고 싶습니다. 근데 key도 또 줄여서 k라고만 쓰고 싶습니다.

5. (관리자) 대시보드 화면

IDEndpointDescriptionRequest PayloadResponse Data
MASTER_2GET /adm/dash월별 방문자 집계, 총 방문자 수- t: string- code: number
- cntVisitor: number
- cntContents: TCntContents
- cntLike: TCntLike

Request Payload의 t
관리자 로그인 성공 시 응답 받는 세션유지용 토큰입니다.

6. (관리자) 컨텐츠 목록 화면

컨텐츠 내용을 보는 화면은 이미 있으니 (관리자에는 없지만) 컨텐츠 제목들 출력, 수정, 삭제만 하고 싶습니다.
수정은 수정페이지로 내비게이팅하고, 출력을 위한 GET 요청, 삭제를 위한 POST 요청만 필요합니다.

IDEndpointDescriptionRequest PayloadResponse Data
CONTENT_4POST /adm/remove컨텐츠 삭제- cid: number
- t: string
- code: number
- msg: string

+ CONTENT_1 (특정 카테고리에 해당하는 컨텐츠 목록)
+ CATEGORY_1 (카테고리 이름 목록)

7. (관리자) 컨텐츠 작성 및 수정 화면

컨텐츠에 이미지를 넣어야 하는데, 그 이미지는 서버에 따로 업로드 시킨 후 서버 컴퓨터에 저장된 경로를 받아와야 합니다. 받아온 경로를 Markdown 텍스트 안에 넣어줘야 하죠. 따라서 컨텐츠를 업로드하는 요청 외에도 이미지를 업로드하는 요청이 필요합니다.

컨텐츠 업로드 시 카테고리까지 지정해야겠죠? 카테고리 이름들을 가져와서 HTML Select태그에 넣어줌으로써 카테고리를 지정할 수 있도록 합시다.
근데 여기서 또 생각할 점.
컨텐츠를 다 작성했고 카테고리를 지정하려고 하는데 새로운 카테고리에 이 컨텐츠를 넣고 싶습니다. 근데 이미 있는 카테고리 안에서만 선택할 수 있으면 안되겠죠? 새로운 카테고리를 추가할 수 있게 해야합니다.

두 가지 방법이 생각나는데요.
1. 서버에 컨텐츠 등록 요청을 보낼 때 category값을 넣어 줄 건데, 서버에서는 이 카테고리가 새로운 놈이면 DB에 추가한 후에 컨텐츠를 저장합니다.
2. 화면에서 새로운 카테고리를 입력해서 서버에 새로운 카테고리가 추가되도록 요청을 보냅니다. 이후 등록 요청을 보내면 서버는 이미 DB에 존재하는 카테고리만 입력값으로 받는다고 판단합니다.

  • 1번은 서버에 요청하는 횟수가 한번입니다.
  • 2번은 서버에 요청하는 횟수가 두번입니다.

그러면 1번이 더 좋은 걸까요? 무작정 그렇다고 판단하긴 이릅니다.

1번으로 하면 '새로운 카테고리'말고 '기존 카테고리'에 컨텐츠를 추가하는 경우에도 쓸데없는 카테고리 검색 작업을 수행할 겁니다.

새로운 카테고리에 컨텐츠를 추가하는 빈도 vs 기존 카테고리에 추가하는 빈도

당연히 후자가 훨씬 많습니다. 고로, 기존 카테고리에 추가하는 케이스에 초점을 두고 판단해야 합니다.

2번으로 갑니다. = 새로운 카테고리로 컨텐츠를 등록할 거면, 서버에 카테고리를 추가하는 요청을 따로 보냅니다.

IDEndpointDescriptionRequest PayloadResponse Data
CONTENT_5POST /adm/content컨텐츠 등록 및 수정- cid (수정 시): number
- t: string
- content: string
- category: string
- code: number
- msg: string
CONTENT_6POST /adm/pic이미지 업로드- cid (수정 시): number
- t: string
- pic: string (base64)
- code: number
- msg: string
- path: string
CATEGORY_4POST /adm/add-category카테고리 추가- t: string
- category: string
- code: number
- msg: string

+ CATEGORY_1 (카테고리 이름 목록)
+ CONTENT_2 (컨텐츠 상세 정보) (수정 시)

8. (관리자) 카테고리 편집 화면

IDEndpointDescriptionRequest PayloadResponse Data
CATEGORY_2GET /adm/category전체 카테고리의 상세 정보- t: string
- categories: TCategoryDetail[]
CATEGORY_3POST /adm/category수정된 카테고리 반영- t: string
- categories: TCategoryDetail[]
- code: number
- msg: string

최종 API 문서

다 썼습니다 이제.. 흩어져 있는 API 문서 내용들을 하나로 합쳐줍니다.
근데 그냥 ID 순으로 합치면 관리자용 API랑 일반사용자용 API랑 뒤죽박죽 섞이니까 일반사용자용 먼저 ID순으로 쭉 쓰고, 그 다음 관리자용 API를 ID순으로 쭉 쓰겠습니다.

IDEndpointDescriptionRequest PayloadResponse Data
MAIN_1GET /main방문자 수와 HOT 컨텐츠-- cntToday: number
- cntTotal: number
- contentPreviewsByHit: TContentPreview[]
- contentPreviewsByLike: TContentPreview[]
CATEGORY_1GET /categories카테고리 이름 목록-- categories: string[]
CONTENT_1GET /contents특정 카테고리에 해당하는 컨텐츠 목록 (요약 정보)- category: string
- offset: string
- limit: string
- contentPreviews: TContentPreview[]
CONTENT_2GET /content특정 컨텐츠 상세 정보와 이전/다음 컨텐츠 링크- cid: string- contentData: TContent
- prevContentPreview: TContentPreview
- nextContentPreview: TContentPreview
CONTENT_3POST /like특정 게시물에 따봉- cid: number- code: number
- msg: string
CATEGORY_2GET /adm/category전체 카테고리의 상세 정보- t: string
- categories: TCategoryDetail[]
CATEGORY_3POST /adm/category수정된 카테고리 반영- t: string
- categories: TCategoryDetail[]
- code: number
- msg: string
CATEGORY_4POST /adm/add-category카테고리 추가- t: string
- category: string
- code: number
- msg: string
CONTENT_4POST /adm/remove컨텐츠 삭제- cid: number
- t: string
- code: number
- msg: string
CONTENT_5POST /adm/content컨텐츠 등록 및 수정- cid (수정 시): number
- t: string
- content: string
- category: string
- code: number
- msg: string
CONTENT_6POST /adm/pic이미지 업로드- cid (수정 시): number
- t: string
- pic: string (base64)
- code: number
- msg: string
- path: string
MASTER_1POST /adm/auth관리자 인증 (로그인)- k: string- token: string OR null
- code: number
MASTER_2GET /adm/dash월별 방문자 집계, 총 방문자 수- t: string- code: number
- cntVisitor: TVisitor
- cntContents: TCntContents
- cntLike: TCntLike

다음엔 DB를 설계합니다. 그리고 바로 서버 코딩할 텐데, 빨리 코딩하고 싶어요..

1개의 댓글

comment-user-thumbnail
2023년 7월 28일

정말 잘 보고 가요

답글 달기