Spring Boot
강의

#5 - Controller에 들어가기 전에 반드시 알아야 할 것

중년개발자
중년개발자

@loxo

22일 전

32

API URL 네이밍 규칙과 리소스 중심 백엔드 설계

왜 Controller 코드를 쓰기 전에 URL부터 배워야 할까?

초보 개발자는 보통 이렇게 시작한다.

text
/login /getUser /saveOrder /updateProfile

처음엔 아무 문제 없어 보인다. 하지만 API가 10개, 50개, 200개로 늘어나는 순간 이 백엔드는 스스로 설명하지 못하는 시스템이 된다.

👉 URL은 단순한 주소가 아니라, 백엔드의 사고방식이 드러나는 설계 문서다.

Controller는 코드이기 전에 계약(Contract) 이다.


1. 업계 표준의 핵심: "행동이 아니라 리소스를 표현한다"

❌ 잘못된 사고 (동사 중심)

text
/getUser /createUser /deleteUser

이 설계는 항상 질문을 만든다.

  • getUser는 조회인가? 상세인가?
  • createUser는 POST인가? GET인가?
  • deleteUser는 여러 개면?

✅ 올바른 사고 (리소스 중심)

text
/users /users/{id}

무엇(What)을 다루는지가 URL에 드러난다.

행동은 URL이 아니라 HTTP Method가 표현한다.


2. HTTP Method는 "의미"를 가진다

Method의미예시
GET조회사용자 목록 조회
POST생성사용자 생성
PUT전체 수정사용자 정보 전체 수정
PATCH부분 수정사용자 비밀번호 변경
DELETE삭제사용자 삭제

👉 URL이 같아도 Method가 다르면 전혀 다른 API다.

text
GET /users -> 사용자 목록 조회 POST /users -> 사용자 생성 GET /users/1 -> 1번 사용자 조회 PATCH /users/1 -> 1번 사용자 일부 수정 DELETE /users/1 -> 1번 사용자 삭제

이 구조만으로도 API 문서의 70%는 설명이 끝난다.


3. 리소스는 항상 "명사" + "복수형"

왜 복수형인가?

  • /user 는 한 명인지, 개념인지 모호하다
  • /users집합(Collection) 을 의미한다
text
/users (사용자라는 자원 전체) /orders (주문 리소스) /products (상품 리소스)

👉 URL만 보고도 데이터 구조가 그려져야 한다.


4. 계층 구조는 "소유 관계"를 표현한다

예시: 사용자의 주문

text
/users/1/orders

의미:

1번 사용자가 소유한 주문들

text
/users/1/orders/10

의미:

1번 사용자의 10번 주문

❗ 이런 URL은 권한 검사, 비즈니스 규칙 설계에 직접적인 힌트를 준다.


5. URL에 절대 넣지 말아야 할 것들

❌ 동사

text
/create /update /process

❌ 상태

text
/complete /cancel /approve

👉 이런 것들은 대부분 비즈니스 행위이며, Controller가 아니라 Service의 책임이다.

상태 변경은 보통 이렇게 표현한다.

text
POST /orders/1/cancel

이때 cancel은 리소스가 아니라 행위 엔드포인트이며, 정말 필요한 경우에만 예외적으로 허용한다.

✅ 정말 예외적으로 허용되는 대표적인 케이스

  1. 되돌릴 수 없는 상태 전이

    • 주문 취소, 결제 환불, 계정 탈퇴처럼
    • "한 번 실행되면 이전 상태로 돌아갈 수 없는" 행위
    text
    POST /orders/1/cancel POST /payments/10/refund POST /accounts/5/withdraw
  2. 비동기 프로세스의 시작점

    • 단순 CRUD가 아니라 "처리 시작"을 의미할 때
    text
    POST /reports/1/generate POST /videos/3/encode
  3. 도메인 용어 자체가 행위인 경우

    • 도메인에서 이미 동사로 굳어진 개념
    text
    POST /auth/login POST /auth/logout

👉 이런 경우에도 공통 원칙은 같다.

  • 항상 POST
  • 단일 리소스에 귀속
  • 남용하면 설계가 무너진다

6. Spring Boot 4에서 Controller URL 기본 형태

java
@RestController @RequestMapping("/users") class UserController { @GetMapping public List<UserResponse> list() {} @GetMapping("/{id}") public UserResponse detail(@PathVariable Long id) {} @PostMapping public void create(@RequestBody CreateUserRequest req) {} }

👉 클래스 레벨 = 리소스 👉 메서드 레벨 = HTTP Method

URL 설계가 깔끔하면 Controller 코드는 자동으로 정리된다.


7. Next.js App Router와의 URL 사고방식 연결

Next.js App Router의 핵심 철학

text
app/ └─ users/ ├─ page.tsx -> /users (목록 화면) ├─ new/ │ └─ page.tsx -> /users/new (등록 화면) └─ [id]/ ├─ page.tsx -> /users/1 (상세 화면) └─ edit/ └─ page.tsx -> /users/1/edit (수정 화면)

Next.js 역시 리소스 중심이다.

  • 폴더 = URL
  • 화면은 URL의 상태 표현(View) 이다

등록 화면은 어떻게 설계할까?

프론트엔드 (화면)

text
/users/new

의미:

사용자 리소스를 새로 생성하기 위한 화면

  • 아직 ID가 없다
  • 그래서 /users/{id} 구조를 쓰지 않는다

백엔드 (API)

text
POST /users

👉 화면 URL과 API URL은 역할이 다르다

  • 화면: 사용자의 행동 흐름
  • API: 리소스의 생성

수정 화면은 어떻게 설계할까?

프론트엔드 (화면)

text
/users/1/edit

의미:

1번 사용자 리소스를 수정하기 위한 화면

백엔드 (API)

text
PATCH /users/1
  • 화면은 edit
  • API는 여전히 리소스 중심

👉 editUI 개념이지, API 개념이 아니다.


초보자가 가장 헷갈리는 포인트

❌ API에도 이렇게 만들고 싶어진다

text
POST /users/edit POST /users/update

👉 이 순간 REST 설계는 무너진다.


✅ 올바른 분리 사고

구분URL 예시책임
화면/users/new사용자 입력
화면/users/1/edit수정 UX
APIPOST /users생성
APIPATCH /users/1변경

프론트의 new, edit화면 상태
백엔드의 POST, PATCH는 데이터 상태 변화


왜 화면 URL은 동사를 허용하는가?

결론부터 말하면 화면은 동사를 허용한다.
하지만 그 이유는 API와 완전히 다르다.


1. API URL은 "시스템에게 하는 명령"이다

API URL은 시스템에게 이렇게 말하는 것이다.

"이 리소스를 어떻게 변경해라"

그래서 API 세계에서는

  • URL = 명사 (대상)
  • 행동 = HTTP Method
text
PATCH /users/1

의미:

1번 사용자 리소스를 수정하라

👉 여기서 edit 같은 동사가 들어오면

  • 명령이 중복되고
  • 책임이 섞이며
  • REST 의미가 붕괴된다

2. 화면 URL은 "사용자의 상태를 설명"한다

화면 URL은 시스템 명령이 아니다.

text
/users/1/edit

이 URL이 의미하는 것은 단 하나다.

"사용자는 지금 1번 사용자를 편집 중이다"

여기서 edit

  • 데이터 변경 ❌
  • 화면 모드(state)

3. 화면은 왜 동사를 허용해야 하는가?

이유 1. 화면에는 "중간 상태"가 존재한다

  • 입력 중
  • 검토 중
  • 아직 저장하지 않음
text
/users/new /users/1/edit

이 상태들은

  • 아직 어떤 데이터도 변경하지 않았고
  • 그래서 API 개념이 될 수 없다

이유 2. 새로고침 · 북마크 · 공유가 가능해야 한다

text
/users/1/edit
  • 새로고침 → 여전히 수정 화면
  • URL 복사 → 같은 수정 화면

👉 이것이 가능한 이유는
화면 URL이 명령이 아니라 위치(Location)이기 때문이다.

API라면 절대 허용되면 안 되는 특성이다.


이유 3. 화면은 UX 언어, API는 도메인 언어다

  • new, edit → 사람이 이해하기 쉬운 UX 언어
  • POST, PATCH → 시스템이 이해하는 도메인 언어

이 둘을 섞으면

  • 프론트는 불편해지고
  • 백엔드는 혼란스러워진다

4. 정확한 역할 분리 정리

구분동사의 의미
화면 URL상태 (지금 무엇을 하고 있는가)
API URL❌ 금지 (행위는 HTTP Method의 책임)

한 문장으로 정리

화면은 동사를 "상태"로 쓰고,
API는 동사를 "명령"으로 쓰기 때문에 허용하지 않는다.

이 경계를 이해하면

  • REST 설계가 흔들리지 않고
  • Next.js App Router와 Spring Controller가
  • 같은 사고 체계 위에서 자연스럽게 연결된다.

👉 프론트와 백엔드가 같은 URL 언어를 쓰되, 같은 책임을 지지는 않는다.


8. 프론트 & 백엔드 URL 일치의 중요성

잘 맞는 경우

text
Frontend: /users/1 Backend : GET /users/1
  • 설명 필요 없음
  • API 문서 최소화
  • 협업 비용 감소

안 맞는 경우

text
Frontend: /userDetail?id=1 Backend : /getUser

👉 항상 추가 설명이 필요해진다.


9. 초보 개발자가 가장 많이 하는 실수 TOP 3

❌ 1. URL에 모든 의미를 담으려 한다

URL은 명사만 말한다. 판단은 Service가 한다.

❌ 2. Controller가 비즈니스 언어가 된다

Controller는 입구다. 규칙은 안쪽에서 결정된다.

❌ 3. 프론트 구조에 맞춰 URL을 설계한다

URL은 화면이 아니라 데이터와 규칙 기준이다.


10. 한 문장으로 정리

좋은 API URL은 설명이 필요 없다.

  • 명사다
  • 복수형이다
  • 계층이 보인다
  • HTTP Method만으로 행동이 드러난다

다음 단계에서는 이 URL 설계를 기준으로 실제 Controller 구현을 시작한다.

그때부터 코드는 더 이상 어렵지 않다. 이미 설계에서 절반은 끝났기 때문이다.

목차

댓글 1

Ctrl + Enter를 눌러 등록할 수 있습니다
※ AI 다듬기는 내용을 정제하는 보조 기능이며, 최종 내용은 사용자가 확인해야 합니다.