본문 바로가기
Java/참고자료

[Java] API 서버 개발 기본

by SeungyubLee 2026. 5. 5.


Spring으로 API 서버를 개발할 때 가장 중요한 건 딱 하나이다.

이 서버는 "화면(View)"을 반환하는 서버인가?
아니면 "데이터(JSON)"를 반환하는 서버인가?

요즘 대부분의 시스템은 프론트엔드(React, Vue 등)와 분리된 구조이기 때문에

Spring 서버는 거의 항상 JSON 데이터를 반환하는 API 서버 역할을 한다.

이 글에서는 API 서버 개발에 필요한 핵심 개념만 빠르게 정리한다.


@Controller vs @RestController

API 서버라면 → 거의 무조건 @RestController 사용

 

▶ @Controller

@Controller
@GetMapping("/home")
public String home() {
    return "home";
}
"home"이라는 데이터를 반환하는 게 아니라
home.html / home.jsp 같은 View를 찾아서 반환

 

@Controller + @ResponseBody

@Controller에서도 API 응답이 가능하다.

기존 MVC 프로젝트에서 일부 API만 추가할 때, 특정 메서드만 JSON 응답이 필요할 때 사용

@Controller
public class UserController {

    @ResponseBody
    @GetMapping("/api/user")
    public User getUser() {
        return new User(1L, "이승엽");
    }
}
{
  "id": 1,
  "name": "이승엽"
}

 

@ResponseBody를 붙이면 해당 메서드의 return 값이 View가 아니라 Response Body(JSON)로 반환된다.

 

@RestController

(1) 문자열 응답

@RestController
@GetMapping("/api/hello")
public String hello() {
    return "hello";
}
hello

 

(2) JSON 응답 (권장 형태)

@RestController
@GetMapping("/api/user")
public User user() {
    return new User(1L, "이승엽");
}
{
  "id": 1,
  "name": "이승엽"
}

 

RestController는 return 값을 그대로 Response Body로 보낸다. 객체를 반환하면 자동으로 JSON으로 변환된다.

 

※ 핵심 정리
@Controller → View 반환
@RestController → JSON(Response Body) 반환
@Controller + @ResponseBody = @RestController와 동일

요청 파라미터 3가지

중복되는 설명을 줄이기 위해 아래와 같은 기본 구조를 기준으로 설명한다.

@RestController
@RequestMapping("/api/users")
public class UserController {
}

즉, 모든 API의 기본 경로는

/api/users

 

▶ @PathVariable (경로 변수) : 리소스를 식별할 때 사용

GET /api/users/10
@GetMapping("/{userId}")
public User getUser(@PathVariable Long userId)
URL의 10 → userId로 전달됨
→ 10번 유저 조회

 

@RequestParam (쿼리 파라미터) : 검색, 조건, 옵션에 사용

GET /api/users?keyword=lee&page=1
@GetMapping
public List<User> getUsers(
    @RequestParam String keyword,
    @RequestParam int page
)
keyword, page 값은 URL 뒤에서 전달됨
→ 검색 및 페이징 조건

 

▶ @RequestBody (요청 Body) : JSON 데이터를 전달할 때 사용

POST /api/users
Content-Type: application/json

{
  "name": "이승엽",
  "email": "lee@example.com"
}
@PostMapping
public void createUser(@RequestBody UserRequest request)
요청 Body(JSON)가 UserRequest 객체로 변환됨
→ 생성/수정 데이터

 

※ 핵심 정리
@PathVariable → URL 경로에 포함된 값 (식별자)
@RequestParam → URL 뒤에 붙는 값 (조건)
@RequestBody → 요청 Body에 담긴 JSON 데이터

HTTP Method

▶ 기본 개념

GET     → 조회
POST    → 생성
PUT     → 전체 수정
PATCH   → 일부 수정
DELETE  → 삭제

 

▶ Mapping 어노테이션과의 관계

Spring에서는 HTTP Method에 따라 다음과 같은 어노테이션을 사용한다.

GET     → @GetMapping
POST    → @PostMapping
PUT     → @PutMapping
PATCH   → @PatchMapping
DELETE  → @DeleteMapping

 

@RequestMapping : 모든 매핑의 기본이 되는 어노테이션

위에서 사용한 Mapping 어노테이션들은 사실 아래의 축약형이다.

@GetMapping("/users")
@RequestMapping(value = "/users", method = RequestMethod.GET)

@PostMapping("/users")
@RequestMapping(value = "/users", method = RequestMethod.POST)

@PutMapping("/users")
@RequestMapping(value = "/users", method = RequestMethod.PUT)

@PatchMapping("/users")
@RequestMapping(value = "/users", method = RequestMethod.PATCH)

@DeleteMapping("/users")
@RequestMapping(value = "/users", method = RequestMethod.DELETE)

 

@RequestMapping을 컨트롤러 클래스 위에 선언하면 해당 컨트롤러의 모든 API에 공통 URL 경로(prefix)가 적용된다.

@RestController
@RequestMapping("/api/users")
public class UserController {
}

이 경우

@GetMapping("/{id}")

실제 요청 URL

/api/users/{id}

정리

클래스 위 @RequestMapping → 공통 경로
메서드 위 Mapping → 개별 API 경로

 

▶ 실무에서의 사용 패턴

GET
- 데이터 조회
- 주로 @PathVariable, @RequestParam 사용
- @RequestBody는 거의 사용하지 않음

POST
- 데이터 생성
- @RequestBody를 가장 많이 사용

PUT
- 데이터 전체 수정
- @PathVariable + @RequestBody 조합

PATCH
- 데이터 일부 수정
- @PathVariable + @RequestBody 조합

DELETE
- 데이터 삭제
- 주로 @PathVariable 사용

 

※ 핵심 정리
@RequestMapping → 모든 매핑의 기본
@GetMapping, @PostMapping 등 → HTTP Method별 축약형 (실무에서 주로 사용)
클래스 위 @RequestMapping → 공통 URL prefix 설정

응답 방식

API 서버 = 거의 100% JSON(Response Body)

 

기본 응답 (자동 JSON 변환)

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
    return new User(1L, "이승엽");
}
Java 객체(User)를 반환하면

Spring이 자동으로
→ JSON 형태로 변환해서 응답
{
  "id": 1,
  "name": "이승엽"
}
Spring 내부에서 Jackson 라이브러리를 사용해
객체 → JSON 변환을 자동 처리

 

실무 방식 (ResponseEntity)

@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    return ResponseEntity.ok(new User(1L, "이승엽"));
}
ResponseEntity란?

HTTP 응답 전체를 제어하는 객체

- Body (데이터)
- Status (200, 404 등)
- Header
※ 사용 예시
return ResponseEntity.ok(user);               // 200 OK
return ResponseEntity.status(201).body(user); // 201 Created
return ResponseEntity.noContent().build();    // 204 No Content

장점 : 응답 상태 코드를 명확하게 제어 가능 → 실무에서 표준적으로 사용

axios + Controller 예시

기본 컨트롤러 구조는 다음을 기준으로 한다.

@RestController
@RequestMapping("/api/users")
public class UserController {
}

 

(1) @PathVariable만 사용

GET /api/users/10
axios.get('/api/users/10');
@GetMapping("/{userId}")
public User getUser(@PathVariable Long userId) {
    return userService.getUser(userId);
}
10번 유저 조회 (식별자 역할)

 

(2) @RequestParam만 사용

GET /api/users?keyword=lee&page=1
axios.get('/api/users', {
  params: {
    keyword: 'lee',
    page: 1
  }
});
@GetMapping
public List<User> getUsers(
        @RequestParam String keyword,
        @RequestParam int page
) {
    return userService.getUsers(keyword, page);
}
검색 및 페이징 조건

 

(3) @RequestBody만 사용

POST /api/users
Content-Type: application/json

{
  "name": "이승엽",
  "email": "lee@example.com"
}
axios.post('/api/users', {
  name: '이승엽',
  email: 'lee@example.com'
});
@PostMapping
public void createUser(@RequestBody UserRequest request) {
    userService.createUser(request);
}
생성/수정할 실제 데이터

 

(4) @PathVariable + @RequestParam

GET /api/users/10/orders?status=PAID&page=1
axios.get('/api/users/10/orders', {
  params: {
    status: 'PAID',
    page: 1
  }
});
@GetMapping("/{userId}/orders")
public List<Order> getOrders(
        @PathVariable Long userId,
        @RequestParam String status,
        @RequestParam int page
) {
    return orderService.getOrders(userId, status, page);
}
10번 유저의 주문 중에서
status 조건으로 필터링 후 조회

 

(5) @PathVariable + @RequestBody

PUT /api/users/10
Content-Type: application/json

{
  "name": "이승엽",
  "email": "lee@example.com"
}
axios.put('/api/users/10', {
  name: '이승엽',
  email: 'lee@example.com'
});
@PutMapping("/{userId}")
public void updateUser(
        @PathVariable Long userId,
        @RequestBody UserRequest request
) {
    userService.updateUser(userId, request);
}
10번 유저를 대상으로
Body 데이터로 수정

 

(6) @RequestParam + @RequestBody

실제로는 많지는 않지만 특정 케이스에서 사용

POST /api/users?notify=true
Content-Type: application/json

{
  "name": "이승엽",
  "email": "lee@example.com"
}
axios.post('/api/users?notify=true', {
  name: '이승엽',
  email: 'lee@example.com'
});
@PostMapping
public void createUser(
        @RequestParam boolean notify,
        @RequestBody UserRequest request
) {
    userService.createUser(request, notify);
}
유저 생성 + 옵션 처리 (알림 여부 등)

 

(7) @PathVariable + @RequestParam + @RequestBody

POST /api/users/10/orders?urgent=true
Content-Type: application/json

{
  "productId": 1,
  "quantity": 2
}
axios.post('/api/users/10/orders', {
  productId: 1,
  quantity: 2
}, {
  params: {
    urgent: true
  }
});
@PostMapping("/{userId}/orders")
public void createOrder(
        @PathVariable Long userId,
        @RequestParam boolean urgent,
        @RequestBody OrderRequest request
) {
    orderService.createOrder(userId, urgent, request);
}
userId → 누구의 주문인지 (대상)
urgent → 옵션 조건
request → 실제 주문 데이터
※ 핵심 정리
@PathVariable → 대상
@RequestParam → 조건
@RequestBody → 데이터

API 설계는 "대상 + 조건 + 데이터" 구조로 이해하면 쉽다.

핵심 요약

1. API 서버 → @RestController 사용

2. 요청 데이터 (조합 가능)
- @PathVariable → 식별자
- @RequestParam → 조건
- @RequestBody → JSON 데이터

3. HTTP Method
GET → 조회 (주로 @PathVariable, @RequestParam 사용)
POST → 생성 (주로 @RequestBody 사용)
PUT → 전체 수정 (주로 @PathVariable + @RequestBody 사용)
PATCH → 일부 수정 (주로 @PathVariable + @RequestBody 사용)
DELETE → 삭제 (주로 @PathVariable 사용)
※ 위 조합만 가능한 것이 아니라, 상황에 따라 다르게 조합될 수 있음

4. 응답
→ 객체 반환 시 자동 JSON 변환
→ 실무는 ResponseEntity 사용

5. 핵심 문장
"RestController면 return은 거의 무조건 Body"