
📌 들어가며
안녕하세요! 멀티캠퍼스 유레카 3기 백엔드 과정을 수강 중인 개발자 지망생입니다.
오늘은 백엔드 개발자라면 반드시 알아야 할 REST API 설계를 배웠어요! 그동안 API를 만들긴 했지만, "왜 GET을 써야 하지?", "URL을 어떻게 만들어야 하지?"라는 고민이 많았는데, 오늘 수업을 듣고 나니 설계 원칙이 명확해졌습니다!
선생님이 수업 시작할 때 이렇게 말씀하셨어요.
"API는 개발자를 위한 UI다. 사용자를 위한 화면을 예쁘게 만들듯이, 개발자를 위한 API도 깔끔하고 직관적으로 만들어야 한다!"
특히 HTTP 메서드의 역할, 상태 코드의 중요성, RESTful한 URI 설계를 배우면서, 지금까지 내가 만든 API가 얼마나 엉망이었는지 깨달았어요 😅
🎯 Today I Learned
✅ REST API란 무엇인가?
✅ RESTful API 6가지 설계 원칙
✅ HTTP 메서드 (GET, POST, PUT, PATCH, DELETE)
✅ HTTP 상태 코드 완벽 정리
✅ URI 설계 규칙
✅ 실무 API 설계 예제
🤔 REST API, 왜 필요할까?


API 설계가 엉망이면?
상황: 쇼핑몰 프로젝트 API
[잘못된 설계]
GET /getUser?id=1 ← 동사 사용 ❌
POST /deleteUser ← POST로 삭제? ❌
GET /user/create ← GET으로 생성? ❌
POST /updateUserPassword ← 의미 불명확 ❌
결과:
- 다른 개발자가 이해 못함
- 유지보수 지옥
- 버그 양산
RESTful하게 설계하면:
[올바른 설계]
GET /users/1 ← 사용자 조회 ✅
DELETE /users/1 ← 사용자 삭제 ✅
POST /users ← 사용자 생성 ✅
PATCH /users/1/password ← 비밀번호 수정 ✅
결과:
- 직관적으로 이해 가능
- 표준 준수
- 협업 용이
REST란?

REST = REpresentational State Transfer
핵심 개념:
- 자원(Resource)을 URI로 표현
- HTTP 메서드로 행위 표현
- 상태 코드로 결과 전달
🎨 REST API 6가지 설계 원칙

1. Client-Server 구조
클라이언트와 서버는 독립적이어야 함
클라이언트 (프론트엔드)
↓ HTTP 요청
서버 (백엔드)
↓ HTTP 응답
클라이언트
- 클라이언트는 서버 구현 몰라도 됨
- 서버는 클라이언트 UI 몰라도 됨
2. Stateless (무상태)
서버는 클라이언트 상태를 저장하지 않음
❌ 잘못된 예:
요청 1: POST /login → 세션 생성
요청 2: GET /profile → 세션 확인
✅ 올바른 예:
요청 1: POST /login → JWT 토큰 반환
요청 2: GET /profile
Header: Authorization: Bearer {token}
각 요청에 필요한 모든 정보 포함!
3. Cacheable (캐시 가능)
응답은 캐시 가능 여부를 명시
HTTP/1.1 200 OK
Cache-Control: max-age=3600 ← 1시간 캐시 가능
ETag: "abc123"
성능 향상!
4. Layered System (계층화)
클라이언트 → 로드 밸런서 → API 서버 → DB
클라이언트는 최종 서버만 알면 됨
중간 계층 투명
5. Uniform Interface (일관된 인터페이스)
모든 API는 일관된 규칙 따름
/users/1 ← 사용자
/posts/1 ← 게시글
/comments/1 ← 댓글
동일한 패턴!
6. Code on Demand (선택사항)
서버가 클라이언트에 실행 가능한 코드 전달
예: JavaScript를 서버에서 전달
(선택적 제약 조건)
🔨 HTTP 메서드 완벽 정리
HTTP 메서드란?
HTTP 메서드 = 리소스에 대한 행위
자원: /users/1
행위: GET (조회), POST (생성), PUT (수정), DELETE (삭제)

1. GET - 조회
특징:
- 리소스 조회
- Body 없음 (데이터는 Query String)
- 안전(Safe): 서버 상태 변경 안함
- 멱등성(Idempotent): 여러번 호출해도 결과 동일
예시:
GET /users/1
GET /users?page=1&size=10
GET /posts/1/comments
Java 코드:
@RestController
@RequestMapping("/users")
public class UserController {
// 단일 사용자 조회
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
UserDto user = userService.findById(id);
return ResponseEntity.ok(user);
}
// 사용자 목록 조회 (페이징)
@GetMapping
public ResponseEntity<Page<UserDto>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size);
Page<UserDto> users = userService.findAll(pageable);
return ResponseEntity.ok(users);
}
}
2. POST - 생성
특징:
- 새로운 리소스 생성
- Body에 데이터 포함
- 멱등성 없음: 여러번 호출 시 중복 생성
- 생성 성공 시 201 Created 반환
예시:
POST /users
Body: {"name": "김철수", "email": "kim@example.com"}
POST /posts
Body: {"title": "제목", "content": "내용"}
Java 코드:
@PostMapping
public ResponseEntity<UserDto> createUser(@RequestBody @Valid UserCreateDto dto) {
UserDto createdUser = userService.create(dto);
// Location 헤더에 생성된 리소스 URI 포함
URI location = URI.create("/users/" + createdUser.getId());
return ResponseEntity
.created(location)
.body(createdUser);
}
요청/응답 예시:
POST /users HTTP/1.1
Content-Type: application/json
{
"name": "김철수",
"email": "kim@example.com",
"password": "password123"
}
HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/json
{
"id": 123,
"name": "김철수",
"email": "kim@example.com",
"createdAt": "2025-12-30T10:00:00"
}
3. PUT - 전체 수정
특징:
- 리소스 전체 교체
- Body에 전체 데이터 필요
- 멱등성 있음: 여러번 호출해도 결과 동일
- 없으면 생성 가능 (상황에 따라)
예시:
PUT /users/1
Body: {
"name": "김철수",
"email": "kim@new.com",
"phone": "010-1234-5678" ← 모든 필드
}
Java 코드:
@PutMapping("/{id}")
public ResponseEntity<UserDto> updateUser(
@PathVariable Long id,
@RequestBody @Valid UserUpdateDto dto) {
UserDto updatedUser = userService.update(id, dto);
return ResponseEntity.ok(updatedUser);
}
4. PATCH - 부분 수정
특징:
- 리소스 일부만 수정
- Body에 변경할 필드만 포함
- 멱등성 있음 (보통)
- PUT보다 효율적
예시:
PATCH /users/1
Body: {
"email": "new@example.com" ← 이메일만 수정
}
Java 코드:
@PatchMapping("/{id}/password")
public ResponseEntity<Void> updatePassword(
@PathVariable Long id,
@RequestBody PasswordUpdateDto dto) {
userService.updatePassword(id, dto);
return ResponseEntity.noContent().build();
}
5. DELETE - 삭제
특징:
- 리소스 삭제
- Body 보통 없음
- 멱등성 있음: 이미 삭제된 것 다시 삭제해도 동일
- 성공 시 204 No Content
예시:
DELETE /users/1
DELETE /posts/1/comments/5
Java 코드:
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
HTTP 메서드 비교표

📊 HTTP 상태 코드 완벽 가이드
상태 코드란?
상태 코드 = 요청 처리 결과를 숫자로 표현
200번대: 성공
300번대: 리다이렉션
400번대: 클라이언트 오류
500번대: 서버 오류
2XX - 성공
200 OK
GET, PUT, PATCH 성공
GET /users/1
→ 200 OK
201 Created
POST 성공 (리소스 생성됨)
POST /users
→ 201 Created
Location: /users/123
204 No Content
요청 성공, 응답 Body 없음
DELETE /users/1
→ 204 No Content
3XX - 리다이렉션
301 Moved Permanently
리소스 URI가 영구 변경됨
GET /old-api/users/1
→ 301 Moved Permanently
Location: /api/v2/users/1
302 Found / 307 Temporary Redirect
리소스 URI가 임시 변경됨
4XX - 클라이언트 오류
400 Bad Request
잘못된 요청
POST /users
Body: {"name": ""} ← 필수 값 누락
→ 400 Bad Request
Java 코드:
@PostMapping
public ResponseEntity<?> createUser(@RequestBody @Valid UserCreateDto dto,
BindingResult result) {
if (result.hasErrors()) {
Map<String, String> errors = new HashMap<>();
result.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
UserDto user = userService.create(dto);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
401 Unauthorized
인증 필요 (로그인 안됨)
GET /profile
→ 401 Unauthorized
WWW-Authenticate: Bearer
403 Forbidden
권한 없음 (로그인은 했지만 접근 불가)
DELETE /admin/users/1 ← 일반 사용자가 관리자 기능 접근
→ 403 Forbidden
404 Not Found
리소스 없음
GET /users/999 ← 존재하지 않는 ID
→ 404 Not Found
409 Conflict
리소스 충돌
POST /users
Body: {"email": "existing@example.com"} ← 이미 존재하는 이메일
→ 409 Conflict
422 Unprocessable Entity
요청 형식은 맞지만 의미가 틀림
POST /users
Body: {"age": -5} ← 나이가 음수
→ 422 Unprocessable Entity
5XX - 서버 오류
500 Internal Server Error
서버 내부 오류
GET /users/1
→ 500 Internal Server Error
(DB 연결 실패, NullPointerException 등)
502 Bad Gateway
게이트웨이/프록시 오류
503 Service Unavailable
서비스 일시적 이용 불가
GET /users
→ 503 Service Unavailable
Retry-After: 3600
상태 코드 실전 활용
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
ex.getMessage()
);
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(error);
}
@ExceptionHandler(DuplicateResourceException.class)
public ResponseEntity<ErrorResponse> handleConflict(DuplicateResourceException ex) {
ErrorResponse error = new ErrorResponse(
"CONFLICT",
ex.getMessage()
);
return ResponseEntity
.status(HttpStatus.CONFLICT)
.body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse(
"INTERNAL_SERVER_ERROR",
"서버 오류가 발생했습니다"
);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(error);
}
}
🛣️ URI 설계 원칙

1. 명사 사용, 동사 금지
❌ 나쁜 예:
GET /getUsers
POST /createUser
POST /deleteUser
✅ 좋은 예:
GET /users
POST /users
DELETE /users/1
URI는 자원을 표현, 행위는 HTTP 메서드로!
2. 소문자 사용
❌ 나쁜 예:
/Users/1
/getUserProfile
✅ 좋은 예:
/users/1
/user-profiles
3. 언더스코어(_) 대신 하이픈(-) 사용
❌ 나쁜 예:
/user_profiles
/order_items
✅ 좋은 예:
/user-profiles
/order-items
4. 마지막 슬래시(/) 포함하지 않기
❌ 나쁜 예:
/users/
/posts/1/
✅ 좋은 예:
/users
/posts/1
5. 파일 확장자 포함하지 않기
❌ 나쁜 예:
/users.json
/profile.xml
✅ 좋은 예:
/users
Accept: application/json
6. 계층 관계 표현
Collection (복수형)
/users ← 사용자 목록
/posts ← 게시글 목록
Document (단수형)
/users/1 ← 특정 사용자
/posts/1 ← 특정 게시글
하위 리소스
/users/1/posts ← 사용자 1의 게시글들
/posts/1/comments ← 게시글 1의 댓글들
7. 필터링, 정렬, 페이징은 Query String
필터링:
GET /users?status=active
GET /posts?category=tech
정렬:
GET /users?sort=createdAt&order=desc
페이징:
GET /posts?page=1&size=10
복합:
GET /posts?category=tech&sort=createdAt&page=1&size=10
💻 실전 API 설계 예제
쇼핑몰 API 설계

1. 회원 API
회원 가입
POST /users
Body: {"email", "password", "name"}
→ 201 Created
로그인
POST /auth/login
Body: {"email", "password"}
→ 200 OK, Body: {"token"}
내 정보 조회
GET /users/me
Header: Authorization: Bearer {token}
→ 200 OK
회원 정보 수정
PATCH /users/me
Body: {"name", "phone"}
→ 200 OK
회원 탈퇴
DELETE /users/me
→ 204 No Content
2. 상품 API
상품 목록 조회
GET /products?category=electronics&page=0&size=10
→ 200 OK
상품 상세 조회
GET /products/1
→ 200 OK
상품 등록 (관리자)
POST /products
Body: {"name", "price", "stock"}
→ 201 Created
상품 수정 (관리자)
PUT /products/1
Body: {"name", "price", "stock"}
→ 200 OK
상품 삭제 (관리자)
DELETE /products/1
→ 204 No Content
3. 주문 API
주문 생성
POST /orders
Body: {
"items": [
{"productId": 1, "quantity": 2},
{"productId": 2, "quantity": 1}
]
}
→ 201 Created
내 주문 목록
GET /orders?status=pending
→ 200 OK
주문 상세 조회
GET /orders/1
→ 200 OK
주문 취소
DELETE /orders/1
→ 204 No Content
4. 장바구니 API
장바구니 조회
GET /cart
→ 200 OK
장바구니에 상품 추가
POST /cart/items
Body: {"productId": 1, "quantity": 2}
→ 201 Created
장바구니 상품 수량 변경
PATCH /cart/items/1
Body: {"quantity": 3}
→ 200 OK
장바구니 상품 삭제
DELETE /cart/items/1
→ 204 No Content
장바구니 비우기
DELETE /cart
→ 204 No Content
게시판 API 설계 (miniproject2 적용)
Java 코드 전체:
@RestController
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
// 게시글 목록 조회
@GetMapping
public ResponseEntity<Page<PostDto>> getPosts(
@RequestParam(required = false) String category,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
Page<PostDto> posts = postService.findAll(category, keyword, pageable);
return ResponseEntity.ok(posts);
}
// 게시글 상세 조회
@GetMapping("/{id}")
public ResponseEntity<PostDetailDto> getPost(@PathVariable Long id) {
PostDetailDto post = postService.findById(id);
return ResponseEntity.ok(post);
}
// 게시글 작성
@PostMapping
public ResponseEntity<PostDto> createPost(
@RequestBody @Valid PostCreateDto dto,
@AuthenticationPrincipal UserDetails userDetails) {
PostDto createdPost = postService.create(dto, userDetails.getUsername());
URI location = URI.create("/api/posts/" + createdPost.getId());
return ResponseEntity.created(location).body(createdPost);
}
// 게시글 수정
@PutMapping("/{id}")
public ResponseEntity<PostDto> updatePost(
@PathVariable Long id,
@RequestBody @Valid PostUpdateDto dto,
@AuthenticationPrincipal UserDetails userDetails) {
PostDto updatedPost = postService.update(id, dto, userDetails.getUsername());
return ResponseEntity.ok(updatedPost);
}
// 게시글 삭제
@DeleteMapping("/{id}")
public ResponseEntity<Void> deletePost(
@PathVariable Long id,
@AuthenticationPrincipal UserDetails userDetails) {
postService.delete(id, userDetails.getUsername());
return ResponseEntity.noContent().build();
}
// 게시글 좋아요
@PostMapping("/{id}/likes")
public ResponseEntity<Void> likePost(
@PathVariable Long id,
@AuthenticationPrincipal UserDetails userDetails) {
postService.toggleLike(id, userDetails.getUsername());
return ResponseEntity.ok().build();
}
}
@RestController
@RequestMapping("/api/posts/{postId}/comments")
@RequiredArgsConstructor
public class CommentController {
private final CommentService commentService;
// 댓글 목록 조회
@GetMapping
public ResponseEntity<List<CommentDto>> getComments(@PathVariable Long postId) {
List<CommentDto> comments = commentService.findByPostId(postId);
return ResponseEntity.ok(comments);
}
// 댓글 작성
@PostMapping
public ResponseEntity<CommentDto> createComment(
@PathVariable Long postId,
@RequestBody @Valid CommentCreateDto dto,
@AuthenticationPrincipal UserDetails userDetails) {
CommentDto comment = commentService.create(postId, dto, userDetails.getUsername());
return ResponseEntity.status(HttpStatus.CREATED).body(comment);
}
// 댓글 수정
@PatchMapping("/{commentId}")
public ResponseEntity<CommentDto> updateComment(
@PathVariable Long postId,
@PathVariable Long commentId,
@RequestBody @Valid CommentUpdateDto dto,
@AuthenticationPrincipal UserDetails userDetails) {
CommentDto comment = commentService.update(commentId, dto, userDetails.getUsername());
return ResponseEntity.ok(comment);
}
// 댓글 삭제
@DeleteMapping("/{commentId}")
public ResponseEntity<Void> deleteComment(
@PathVariable Long postId,
@PathVariable Long commentId,
@AuthenticationPrincipal UserDetails userDetails) {
commentService.delete(commentId, userDetails.getUsername());
return ResponseEntity.noContent().build();
}
}
💡 오늘 나는 무엇을 잘했는가 (성취)
miniproject2 API를 RESTful하게 리팩토링!
기존에는 이렇게 작성했었어요:
❌ Before:
GET /getPostList
POST /createPost
POST /updatePost
POST /deletePost
오늘 배운 내용을 적용해서 수정했어요:
✅ After:
GET /api/posts ← 목록 조회
POST /api/posts ← 생성
PUT /api/posts/{id} ← 수정
DELETE /api/posts/{id} ← 삭제
GET /api/posts/{id} ← 상세 조회
훨씬 깔끔! 🎉
HTTP 상태 코드를 제대로 사용!
예전에는 모든 응답을 200으로 보내고 Body에 {"success": true}를 넣었는데...
❌ Before:
POST /createPost
→ 200 OK
Body: {"success": true, "data": {...}}
DELETE /deletePost
→ 200 OK
Body: {"success": true}
이제는 적절한 상태 코드를 사용해요:
✅ After:
POST /posts
→ 201 Created
Location: /api/posts/123
DELETE /posts/123
→ 204 No Content
전역 예외 처리 구현!
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(
ResourceNotFoundException ex) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("NOT_FOUND", ex.getMessage()));
}
}
이제 404, 409, 500 같은 에러도 일관되게 처리돼요!
🔧 어떤 문제를 겪었고, 어떻게 개선할까 (개선점)
문제 1: PUT vs PATCH 헷갈림

비밀번호만 변경할 때 PUT을 쓸지 PATCH를 쓸지 헷갈렸어요!
해결 방법:
원칙:
- 전체 교체 → PUT
- 부분 수정 → PATCH
예:
PUT /users/1
{
"name": "김철수",
"email": "kim@example.com",
"phone": "010-1234-5678" ← 전체 필드
}
PATCH /users/1/password
{
"newPassword": "newpass123" ← 일부만
}
→ 비밀번호 변경은 PATCH!
문제 2: 에러 응답 형식이 중구난방
컨트롤러마다 에러 응답 형식이 달랐어요 😅
❌ 컨트롤러 A:
{"error": "Not Found"}
❌ 컨트롤러 B:
{"message": "리소스 없음", "status": 404}
해결 방법:
// 공통 에러 응답 형식 정의
public class ErrorResponse {
private String code;
private String message;
private LocalDateTime timestamp;
// constructor, getters
}
// 사용
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("NOT_FOUND", "사용자를 찾을 수 없습니다"));
문제 3: URI에 동사가 들어가는 경우
문제: 비밀번호 변경, 로그인, 로그아웃은 어떻게?
처음 생각:
POST /changePassword ← 동사 들어감 ❌
POST /doLogin ← 동사 들어감 ❌
개선:
POST /users/me/password ← 리소스 관점
POST /auth/login ← 인증은 예외
POST /auth/logout
개선 계획:
원칙:
1. 최대한 명사로 표현
2. 불가피한 경우 컨트롤 URI 사용
(예: /search, /login, /logout)
📚 오늘 배운 것 (학습)
1. API는 개발자를 위한 UI
웹 UI를 만들 때:
- 버튼 배치 고민
- 색상 통일
- 직관적인 흐름
API를 만들 때도:
- URI 일관성
- 명확한 메서드 선택
- 적절한 상태 코드
똑같이 신경써야 함!
2. HTTP 표준을 활용하자
HTTP가 이미 다 정의해놨음:
- GET: 조회
- POST: 생성
- PUT/PATCH: 수정
- DELETE: 삭제
- 상태 코드: 200, 201, 400, 404...
새로 만들 필요 없음!
3. URI는 리소스를 표현, 행위는 메서드로
❌ /getUserById/1 ← URI에 행위
❌ /createNewPost ← URI에 행위
✅ GET /users/1 ← 메서드로 행위 표현
✅ POST /posts ← 메서드로 행위 표현
4. 상태 코드의 중요성
200만 쓰면:
클라이언트가 Body 파싱해야 성공/실패 구분
적절한 코드 사용하면:
상태 코드만으로 결과 판단 가능
개발자 경험 향상!
😊 좋았던 점 & 😅 아쉬웠던 점
좋았던 점
👍 실제 프로젝트 적용
배운 내용을 바로 miniproject2에 적용해봤더니, 팀원들이 "API가 훨씬 이해하기 쉬워졌어요!"라고!
👍 HTTP 표준의 힘
HTTP 메서드와 상태 코드만 제대로 사용해도 API가 훨씬 명확해진다는 걸 체감했어요.
👍 실무 중심 수업
실제 쇼핑몰, 게시판 예제로 배우니까 바로 써먹을 수 있었어요!
아쉬웠던 점
😢 API 버전 관리는 다루지 못함
/api/v1/users vs /api/v2/users 같은 버전 관리 전략은 시간 관계상 못 배워서 아쉬웠어요.
😢 HATEOAS는 너무 어려움
REST의 완벽한 구현을 위한 HATEOAS 개념이 나왔는데... 솔직히 이해 못했어요 😅
😢 실습 시간 부족
이론은 많이 배웠는데 직접 API 설계해보는 실습 시간이 부족했어요.
🎓 나만의 학습 팁
1. API 설계 체크리스트
✅ URI에 동사 없나?
✅ 복수형 사용했나? (users, posts)
✅ 소문자 사용했나?
✅ 하이픈(-) 사용했나? (user-profiles)
✅ 마지막 슬래시(/) 없나?
✅ HTTP 메서드 적절한가?
✅ 상태 코드 명확한가?
2. 실제 API 분석하기
유명한 API 뜯어보기:
GitHub API:
GET https://api.github.com/users/aaxx98
GET https://api.github.com/repos/{owner}/{repo}
Twitter API:
GET /2/tweets/{id}
POST /2/tweets
어떻게 설계했는지 분석!
3. HTTP 메서드 선택 플로차트
조회만? → GET
새로 만들기? → POST
전체 바꾸기? → PUT
일부만 바꾸기? → PATCH
삭제? → DELETE
4. 상태 코드 외우기
자주 쓰는 것만:
2XX: 성공
200: 일반 성공
201: 생성 성공
204: 성공 (Body 없음)
4XX: 클라이언트 오류
400: 잘못된 요청
401: 인증 필요
403: 권한 없음
404: 없음
409: 충돌
5XX: 서버 오류
500: 서버 오류
5. 추천 도구
API 문서 자동화:

Swagger (SpringDoc OpenAPI)
- @Operation(summary = "사용자 조회")
- @ApiResponse(responseCode = "200")
- 자동으로 API 문서 생성!
API 테스트:


1. Postman
- GUI로 편리
- 테스트 케이스 저장
2. IntelliJ HTTP Client
- .http 파일로 관리
- Git 버전 관리 가능
3. curl
- 터미널에서 빠르게
🚀 다음 학습 목표
📌 단기 목표 (이번 주)
✓ miniproject2 전체 API RESTful하게 리팩토링
✓ Swagger 적용해서 API 문서 자동화
✓ 전역 예외 처리 완성
✓ HTTP 상태 코드 완벽 활용
📌 중기 목표 (이번 달)
✓ API 버전 관리 전략 학습 (/v1, /v2)
✓ HATEOAS 개념 다시 공부
✓ API 페이징, 정렬, 필터링 구현
✓ Rate Limiting (요청 제한) 구현
📌 장기 목표 (부트캠프 기간)
✓ GraphQL vs REST 비교 학습
✓ API Gateway 패턴 학습
✓ MSA 환경에서의 API 설계
✓ 포트폴리오에 API 문서 포함
🎬 마치며
오늘 REST API 설계를 배우기 전에는 "그냥 데이터만 주고받으면 되는 거 아니야?"라고 생각했어요.
하지만 직접 설계해보고 나니 깨달았습니다.
API는 개발자를 위한 사용자 경험(UX)입니다.
좋은 UI가 사용자 경험을 좌우하듯, 좋은 API도 개발자 경험을 좌우해요!
선생님이 마지막에 하신 말씀이 기억에 남아요.
"API는 한 번 만들면 바꾸기 어렵다. 처음부터 잘 설계하는 습관을 들여라. RESTful한 API는 그 시작이다."
특히 HTTP 메서드로 행위를 표현하고, URI로 리소스를 표현하고, 상태 코드로 결과를 전달하는 REST 3요소가 정말 명확했어요!
앞으로는 API를 만들 때 **"이게 RESTful한가?"**를 항상 고민하겠습니다! 💪
깔끔하고 직관적인 API, 오늘부터 만들어봅니다!
💬 여러분의 API 설계 경험은?
GET만 써보셨나요? 아니면 POST로 모든 걸 처리하셨나요? API 설계하면서 겪은 어려움이나 팁이 있다면 댓글로 공유해주세요! 🙏