포스트

REST API란?

REST API란?

REST API 제대로 이해하기 💪

REST API란?

REST API는 Representational State Transfer의 약자로, 자원(Resource) 중심의 API 설계 방식을 의미한다. 쉽게 말해, 서버의 데이터(자원)에 접근하는 규칙을 정해둔 표준이다.

🐥 REST의 핵심 원칙

  1. 클라이언트 - 서버 구조 (Client-Server Architecture)
    • 클라이언트(프론트엔드)는 UI/UX를 담당하고, 서버(백엔드)는 데이터 저장 및 처리를 담당한다.
    • 이렇게 역할이 분리되어야 유지보수와 확장성이 쉽다.
  2. 무상태성 (Stateless)
    • 서버는 클라이언트의 상태(Session)를 보존하지 않는다.
    • 요청마다 필요한 모든 정보를 담아서 보내야 한다.
  3. 캐시 가능성 (Cacheable)
    • GET 요청 같은 경우는 캐시를 활용할 수 있다.
    • 예를 들어, 정적 이미지나 자주 조회하는 목록 API는 캐싱하면 성능이 좋아진다.
  4. 계층화 (Layered System)
    • 클라이언트는 서버가 직접 처리하는지, 다른 서버를 거치는지 알 필요가 없다.
    • 보안, 로드밸런싱, 프록시 서버 등을 자유롭게 끼워 넣을 수 있다.
  5. 균일한 인터페이스 (Uniform Interface)
    • 요청 방법이 일관되어야 한다.
    • 예: /users에서 GET은 사용자 목록 조회, POST는 사용자 생성 -> 직관적으로 이해 가능해야 한다.

✨ HTTP 메서드 활용

REST API는 HTTP 메서드로 행위를 표현한다.

  • GET -> 자원 조회
    • 예: GET /users -> 사용자 목록 가져오기
  • POST -> 자원 생성
    • 예: POST /users -> 새로운 사용자 등록
  • PUT -> 자원 전체 수정 (덮어쓰기
    • 예: PUT /users/1 -> ID가 1인 사용자의 정보를 전부 업데이트
  • PATCH -> 자원 일부 수정
    • 예: PATCH /users/1 -> ID가 1인 사용자 이메일만 수정
  • DELETE -> 자원 삭제
    • 예: DELETE /users/1 -> ID가 1인 사용자 삭제

🥊 실무에서 주의할점

REST API는 이론만 알면 되는게 아니라, 실무에서 지켜야 할 규칙이 있다.

1. URL 설계

  • 명사 사용: 동사가 아니라 자원(명사) 중심으로 작성해야 한다.
    • 잘못된 예: /getUser
    • 올바른 예: /users
  • 계층 구조 반영: 관계가 있으면 계층 구조로 표현한다.
    • 예: /users/1/orders -> ID가 1인 사용자의 주문 목록
  • 복수형 사용: 일반적으로 컬렉션 자원은 복수형으로 사용한다.
    • 예: /users (O), /user (X)

2. 응답 코드 (Status Code)

  • 클라이언트에게 상황을 정확히 알려주려면 HTTP 상태 코드를 올바르게 사용하는게 중요하다.
    • 200 OK: 요청 성공 (조회, 수정 성공 시)
    • 201 Created: 새로운 자원 생성 성공 (POST)
    • 400 Bad Request: 요청이 잘못됨 (파라미터 오류 등)
    • 404 Not Found: 요청한 자원이 없음
    • 500 Internal Server Error: 서버 내부 오류

3. 버전 관리 API는 시간이 지나면서 변경 및 확장될 수 있다. 그래서 보통 버전을 명시한다.

  • 예: /api/v1/users, /api/v2/users
  • 이렇게 해두면 구버전을 사용하는 클라이언트도 안전하게 유지할 수 있다.

코드 예시

사용자(User) 엔티티를 대상으로 등록, 조회, 수정, 삭제를 구현하는 예시를 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
@Getter @Setter
@Table(name = "users")
public class User{
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTIFY)
  private Long id;
  
  private String username;
  private String email;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/users") // 버전 관리 포함
public class UserController{
  private final UserService userService;
  
  // CREATE
  @PostMapping
  public ResponseEntity<UserResponseDto> createUser(@RequestBody UserRequestDto userRequestDto){
    UserResponseDto user = userService.createUser(userRequestDto);
    return ResponseEntity.ok(user);
  }
  
  // READ
  @GetMapping
  public ResponseEntity<List<UserResponseDto>> getUsers(){
    List<UserResponseDto> users = userService.getUsers();
    return ResponseEntity.ok(users);
  }
  
  // READ (단일 조회)
  @GetMapping("/{id}")
  public ResponseEntity<UserResponseDto> getUser(@PathVariable Long id){
    User user = userService.getUser(id);
    if(user == null) throw new NotFoundUserException("사용자를 찾을 수 없습니다.");
    return ResponseEntity.ok(user);
  }
  
  // UPDATE
  @PatchMapping("/{id}")
  public ResponseEntity<UserResponseDto> updateUser(@PathVariable Long id, @RequestBody UserRequestDto userRequestDto){
    User updated = userService.updateUser(id, userRequestDto);
    if(update == null) throw new NotFoundUserException("사용자를 찾을 수 없습니다");
    return ResponseEntity.ok(updated);
  }
  
  // DELETE
  @DeleteMapping("/{id}")
  public ResponseEntity<Void> deleteUser(@PathVariable Long id){
    userService.deleteUser(id);
    return ResponseEntity.noContent().build();
  }
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.