ํ... ์คํ๋ง์ ํ๋ฉด์ ํ๋ ๊ฒ ํ ๋๊ฐ๊ฐ ์๋๋ค.
์ผ๋จ ํ๋ก ํธ์๋ ๊ณต๋ถํด ๋ณด๋ฉด์, ์ํผ๋ฒ ์ด์ค ์ฐ๋ํ์ฌ DB์์ CRUD๋ฅผ ๊ฒฝํํ ๋ฐ ์๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ๋ก ํธ๋ ๋ค ํ ์ ์๋๋ฐ ๋ฐฑ์๋์์ ์ฐจ์ด๊ฐ ๋ช ํํ๊ฒ ์๋ณด์ฌ์ ํ๋ค์๋ค.
ํ์ ๊ฐ๋ฐํ๋ฉด์ ์ ๋ณด์๋ ์ ์, ์ํผ๋ฒ ์ด์ค๊ฐ ๋ฐฑ์๋ ์ญํ ์ ๋์ ๋งก์์ค ์ .
=> DB ํธ๋์ญ์ , ๊ถํ ์ ์ฑ , ๋ก๊น , ๊ฐ์ฌ ๋ฑ์ ์ง์ง ์คํ ์ฃผ์ฒด๋ ์๋ฒ ์ชฝ์ด ํ์ํจ
์์ผํ๋ ๊ฒ ๊ฐ๋ค. ๋ญ๊ฐ ๊ธฐ์ต ์ ํธ ๋๋จธ์ ์๋ ๊ฒ ๊ฐ๊ธฐ๋ ํ๊ณ .
๊ฐ์ ๊ธฐ๋ฅ(์: “์ ์ ์ญ์ ”)์ React(ํ๋ก ํธ) vs Spring(๋ฐฑ์๋)๋ก ๋๋ ์ ๋ณด๋ฉด ์ฐจ์ด๊ฐ ํ ์ ๋ค.
// React ์์
function UserItem({ id }) {
const handleDelete = async () => {
await fetch(`/api/users/${id}`, { method: 'DELETE' });
// ํ๋ฉด์์ ์ ๊ฑฐ(๋ฆฌ์คํธ ๋ค์ ๋ถ๋ฌ์ค๊ฑฐ๋, ๋ก์ปฌ ์ํ์์ ๋นผ๊ธฐ)
};
return <button onClick={handleDelete}>์ญ์ </button>;
}
๋ฒํผ ํด๋ฆญ ์ HTTP ์์ฒญ ์ ์กํ๋ ์ฝ๋
๋ถ๊ฐ๋ฅํ ์ : DB์์ ์ง์ง๋ก ์ง์ฐ๊ธฐ, ๊ถํ ์ฒดํฌ, ํธ๋์ญ์ ์ฒ๋ฆฌ, ๋ก๊ทธ ๊ธฐ๋ก ๋ฑ
(๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์๋๊ตฌ๋ก ๋๊ตฌ๋ ์ง์ ์์ฒญ ๋ณด๋ผ ์ ์๊ธฐ ๋๋ฌธ์, “์ง์ง ์ญ์ ”๋ ์๋ฒ๊ฐ ํ๋จ/์คํํด์ผ ํจ)
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService service;
public UserController(UserService service) {
this.service = service;
}
@DeleteMapping("/{id}") // ← ๋งคํ: DELETE /api/users/3
public void delete(@PathVariable Long id,
@AuthenticationPrincipal UserPrincipal me) {
// 1) ์ธ์ฆ/๊ถํ ์ฒดํฌ
if (!me.isAdmin()) throw new ForbiddenException();
// 2) ๋น์ฆ๋์ค ๋ฃฐ(๊ด๋ จ ๋ฐ์ดํฐ ์ ๋ฆฌ ๋ฑ)
service.deleteUser(id);
// 3) ํธ๋์ญ์
์ผ๋ก DB ๋ณ๊ฒฝ
// 4) ๊ฐ์ฌ ๋ก๊ทธ ๊ธฐ๋ก
}
}
- ์ด๋ค URL + HTTP ๋ฉ์๋(DELETE) → ์ด๋ค ๋ฉ์๋๊ฐ ๋ฐ์์ง ๋งคํ
- ์์ฒญ๊ฐ์ ๊ฒ์ฆ/๊ถํ์ฒดํฌ
- DB ์กฐ์, ํธ๋์ญ์ ๊ด๋ฆฌ
- ์ ํํ ์๋ต ์ฝ๋/๋ฐ๋ ๋ฐํ
- ๋ก๊ทธ, ๋ชจ๋ํฐ๋ง, ์์ธ ์ฒ๋ฆฌ ๋ฑ ์ด์ ์ฑ ์
๋งคํ
๋งคํ์ด๋?
- HTTP ์์ฒญ(์ฃผ์+๋ฉ์๋+๊ธฐํ ์ ๋ณด)์ ์ด๋ค ๋ฉ์๋๋ก ๋ณด๋ผ์ง ์ฐ๊ฒฐํด ๋๋ ๊ท์น.
- ์ฝ๊ฒ ๋งํด ๋ผ์ฐํ , ๊ธธ์ฐพ๊ธฐ ํ์งํ ์ญํ
๋ค์๊ณผ ๊ฐ์ ๋งคํ์ด ์๋ค.
- @RequestMapping
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
์์ ์ฝ๋๋ฅผ ๋ณด๋ฉฐ ์ดํด๋ฅผ ํ๊ธฐ๋ก ํ๋ค.
//๋ด๋ถ์ ์ผ๋ก @Controller + @ResponseBody๊ฐ ํฉ์ณ์ง ์ ๋ํ
์ด์
.
//์ด ํด๋์ค๋ ์น ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์ปจํธ๋กค๋ฌ + ๋ฉ์๋๊ฐ ๋ฆฌํดํ ๊ฑธ ๊ทธ๋๋ก HTTP ์๋ต ๋ฐ๋์ ๋ฃ์
@RestController
//์ด ํด๋์ค ์์ ๋ชจ๋ ๋ฉ์๋ URL ์์ /users๋ฅผ ๋ถ์ฌ๋ผ๋ ๊ณตํต ๊ฒฝ๋ก(prefix)์ง์ .
@RequestMapping("/users")
// ํด๋์ค์ @RequestMapping("/users")
// ๋ฉ์๋์ @GetMapping("/{id}")
// ⇒ ์ค์ URL: GET http://localhost:8080/users/10
// ์ปจํธ๋กค๋ฌ๋ ๊ฒฐ๊ตญ ํด๋์ค๋ก ๋ง๋ค์ด์ผ ํจ.(๋ฉ์๋๋ฅผ ๋ด์ ๊ทธ๋ฆ์ด ํ์)
public class UserController{
//1) ์กฐํ: GET
@GetMapping("{/id}") //GET/user/10
public UserDto getUser(@PathVariable Long id){
// @PathVariable ๋ก URL์ {id} ๊ฐ์ ๋งคํ
return service.findById(id);
}
//2) ์์ฑ: POST
@PostMapping( // POST /users
// ์์ฒญ ๋ฐ๋ ํ์
์ ํ(์ ํ)
consumes = "application/json",
// ์๋ต ํ์
์ ํ(์ ํ)
produces = "application/json"
)
//3) PUT: ์ ์ฒด ์์ (์นํ)
@PutMapping("/{id}") //PUT/users/10
public UserDto replace(@PathVariable Long id, @RequestBody UpdateUserReq req
){
return service.replace(id, req);
}
//4) PATCH: ๋ถ๋ถ์์
@PathMapping("/{id}") //PATCH/users/10
public UserDto patch(@PathVariable Long id, @RequestBody Map<String, Object> fields){
return service.prtialUpdate(id, fields);
}
//5) DELETE: ์ญ์
@DeleteMapping("/{id}") //DELETE/users/10
public void delete(@PathVariable Long id){
service.delete(id);
}
}
์ค์ ์ฃผ์๋ ์๋์ ๊ฐ๋ค
์ค์ URL = ์๋ฒ์ฃผ์(http://localhost:8080) + ํด๋์ค prefix(/users) + ๋ฉ์๋ ๊ฒฝ๋ก("/{id}" ๊ฐ์ ๊ฒ)
๋ฌธ๋ฒ๋ง ๋๊ณ ๋ณด๋ฉด
@RestController
@RequestMapping("/users") // ๊ณตํต prefix
public class UserController {
// 1) ์กฐํ(Read, ๋จ๊ฑด)
@GetMapping("/{id}") // GET /users/{id}
public UserDto getUser(@PathVariable Long id) { ... }
// 2) ์์ฑ(Create)
@PostMapping( // POST /users
consumes = "application/json",
produces = "application/json"
)
public UserDto create(@RequestBody CreateUserReq req) { ... }
// 3) ์ ์ฒด ์์ (Replace)
@PutMapping("/{id}") // PUT /users/{id}
public UserDto replace(@PathVariable Long id,
@RequestBody UpdateUserReq req) { ... }
// 4) ๋ถ๋ถ ์์ (Patch)
@PatchMapping("/{id}") // PATCH /users/{id}
public UserDto patch(@PathVariable Long id,
@RequestBody Map<String, Object> fields) { ... }
// 5) ์ญ์ (Delete)
@DeleteMapping("/{id}") // DELETE /users/{id}
public void delete(@PathVariable Long id) { ... }
}
*์ปจํธ๋กค๋ฌ = ํด๋์ค(๊ทธ๋ฆ).
์์งํ *์ปจํธ๋กค๋ฌ๋ง ์์ด๋ ๋์๊ฐ๊ธด ํ๋ค. ๊ทธ๋ฌ๋ ์ค๋ฌด์์๋ ๋ณดํต
Controller → Service → Repository(DB)๋ก ๋๋๊ณ DTO ๋ก ์์ฒญ/์๋ต ํ์์ ๋ถ๋ฆฌํ๋ค.
๊ตณ์ด ๋๋๋ ์ด์ ๋
- Controller: HTTP์ฒ๋ฆฌ(๋งคํ, ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ, ์ํ์ฝ๋ ๋ฑ)๋ง
- Service: ๋น์ฆ๋์ค ๋ก์ง(๊ณ์ฐ, ์ ์ฑ , ํธ๋์ญ์ )
- Repository: DB์ ๊ทผ
- DTO: HTTP์ฉ ๋ฐ์ดํฐ ํฌ๋งท(ํ๋๋ช , ๊ฒ์ฆ)๊ณผ ๋ด๋ถ ๋๋ฉ์ธ์ ๋ถ๋ฆฌ
=> ์ ๋๋๋ฉด ์ปจํธ๋กค๋ฌ ์์ ๋ก์ง/DB์ฝ๋ ๋ค ๋๋ ค๋ฃ์ด์ ๊ธ๋ฐฉ ์ง์ฅ ๋จ
๋๋จธ์ง ์์์ฝ๋๋ ๊ฐ์ด ๋ค๋ค๋ณด๊ฒ ๋ค
Service
// src/main/java/com/example/demo/user/UserService.java
package com.example.demo.user;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class UserService {
// ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ด DB์ฒ๋ผ ์ฌ์ฉํ๊ธฐ ์ํ Map (key: id, value: UserDto)
private final Map<Long, UserDto> store = new HashMap<>();
// ID๋ฅผ 1์ฉ ์ฆ๊ฐ์ํค๋ฉฐ ๋ฐ๊ธํ๊ธฐ ์ํ ์ฐ๋ ๋ ์์ ํ ์นด์ดํฐ
private final AtomicLong seq = new AtomicLong(1);
// ๋จ๊ฑด ์กฐํ: id๋ก store์์ ์ฐพ์ ๋ฐํ
public UserDto findById(Long id) {
return store.get(id);
}
// ์ ์ฒด ์กฐํ: store์ ๊ฐ๋ค๋ง ๋ฝ์์ List๋ก ๋ฐํ
public List<UserDto> findAll() {
return new ArrayList<>(store.values());
}
// ์์ฑ: ์ ID ๋ฐ๊ธ → DTO ๋ง๋ค๊ณ → store์ ์ ์ฅ → ์ ์ฅ๋ DTO ๋ฐํ
public UserDto create(CreateUserReq req) {
Long id = seq.getAndIncrement();// ํ์ฌ ๊ฐ ๋ฐํ ํ +1
// ์์ฒญ ๋ฐ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก DTO ์์ฑ
UserDto dto = new UserDto(id, req.name(), req.email());
store.put(id, dto);// ๋ฉ๋ชจ๋ฆฌ ์ ์ฅ
return dto;
}
// ์ ์ฒด ์์ (์นํ): ๋ค์ด์จ ๊ฐ์ผ๋ก ํต์งธ๋ก ๋ฎ์ด์ฐ๊ธฐ
public UserDto replace(Long id, UpdateUserReq req) {
UserDto dto = new UserDto(id, req.name(), req.email());
store.put(id, dto);// ํด๋น id์ ๋ฎ์ด์(์์ผ๋ฉด ์๋ก ๋ค์ด๊ฐ)
return dto;
}
// ๋ถ๋ถ ์์ : ์ ๋ฌ๋ fields(Map)์ ์๋ ๊ฐ๋ง ๊ธฐ์กด ๊ฐ์์ ๋ฐ๊ฟ์น๊ธฐ
public UserDto partialUpdate(Long id, Map<String, Object> fields) {
UserDto old = store.get(id);// ๊ธฐ์กด ๋ฐ์ดํฐ ์กฐํ
if (old == null) return null;// ์์ผ๋ฉด null (์ค๋ฌด์์ ์์ธ ๋์ง)
// ์ ๊ฐ ์์ผ๋ฉด ์ ๊ฐ, ์์ผ๋ฉด ๊ธฐ์กด ๊ฐ
String name = (String) fields.getOrDefault("name", old.name());
String email = (String) fields.getOrDefault("email", old.email());
UserDto updated = new UserDto(id, name, email);// ์์ ๋ DTO ์์ฑ
store.put(id, updated); // ์ ์ฅ์์ ๊ฐฑ์
return updated;
}
// ์ญ์ : id๋ก store์์ ์ ๊ฑฐ
public void delete(Long id) {
store.remove(id);
}
}
dto
// src/main/java/com/example/demo/user/dto๋ค.java
package com.example.demo.user;
// Java 17 record ์์ (๊ฐ๋จ DTO)
public record UserDto(Long id, String name, String email) {}
public record CreateUserReq(String name, String email) {}
public record UpdateUserReq(String name, String email) {}
ํฌ์คํธ๋งจ์ผ๋ก ํ์ธ ์
GET http://localhost:8080/users
POST http://localhost:8080/users (JSON ๋ฐ๋)
GET http://localhost:8080/users/1
PUT http://localhost:8080/users/1
PATCH http://localhost:8080/users/1
DELETE http://localhost:8080/users/1
HTTP ํ ๋ฐฉ์ ์ฒ๋ฆฌ ํ๋ฆ
[๋ธ๋ผ์ฐ์ /React] โโHTTP์์ฒญโโ>[Controller]โ>[Service]โ>[Repository/DB]
โ โ โ
(์์ฒญ/์๋ต ์ฒ๋ฆฌ) (๋น์ฆ๋์ค ๋ก์ง)(๋ฐ์ดํฐ ์ ์ฅ/์กฐํ)
โ
[DTO ๋ณํ]
โ
HTTP ์๋ต(JSON ๋ฑ)
๋ค ํ์ ์๊ณ ์ผ๋จ ์๋ ์ฝ๋๋ง ๊ธฐ์ตํด์ผ๊ฒ ๋ค
@RestController
@RequestMapping("/๋ฆฌ์์ค")
public class FooController {
private final FooService service;
public FooController(FooService service){ this.service = service; }
@GetMapping("/{id}")
public FooDto get(@PathVariable Long id){ return service.get(id); }
@PostMapping // ์๋ก๋ง๋ค๊ธฐ๋ผ id๊ฐ ์์
public FooDto create(@RequestBody CreateFooReq req){ return service.create(req); }
@PutMapping("/{id}")
public FooDto replace(@PathVariable Long id, @RequestBody UpdateFooReq req){ return service.replace(id, req); }
@PatchMapping("/{id}")
public FooDto patch(@PathVariable Long id, @RequestBody Map<String,Object> fields){ return service.patch(id, fields); }
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){ service.delete(id); }
}
'Backend > ๐ฑ Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Spring]์์กด๊ด๊ณ ์ฃผ์ (DI) (2) | 2025.07.28 |
|---|---|
| [Spring]Spring Bean (2) | 2025.07.28 |
| [Spring] Layered Architecture (3) | 2025.07.26 |
| [Spring]MVC (4) | 2025.07.23 |
| [Spring] Spring! (1) | 2025.07.22 |