🎯 학습 목표
- 3계층 + DTO로 완전한 CRUD API를 구현한다.
- 적절한 HTTP 상태코드를 반환한다.
- curl로 전체 동작을 검증한다.
📖 개념 설명
앞에서 배운 조각(Controller, Service, Repository, Entity)을 모아 실제로 동작하는 메모 API를 완성합니다. 자원(Memo)에 대해 생성(POST)·조회(GET)·수정(PUT)·삭제(DELETE)를 REST 규칙에 맞게 매핑합니다.
💻 DTO (record)
public record MemoRequest(String title, String body) {}
public record MemoResponse(Long id, String title, String body) {}
💻 Service
@Service
public class MemoService {
private final MemoRepository repo;
public MemoService(MemoRepository repo) { this.repo = repo; }
public MemoResponse create(MemoRequest req) {
Memo m = new Memo();
m.setTitle(req.title()); m.setBody(req.body());
Memo saved = repo.save(m);
return toResponse(saved);
}
public List findAll() {
return repo.findAll().stream().map(this::toResponse).toList();
}
public MemoResponse findOne(Long id) {
Memo m = repo.findById(id)
.orElseThrow(() -> new IllegalArgumentException("없음: " + id));
return toResponse(m);
}
public MemoResponse update(Long id, MemoRequest req) {
Memo m = repo.findById(id)
.orElseThrow(() -> new IllegalArgumentException("없음: " + id));
m.setTitle(req.title()); m.setBody(req.body());
return toResponse(repo.save(m));
}
public void delete(Long id) { repo.deleteById(id); }
private MemoResponse toResponse(Memo m) {
return new MemoResponse(m.getId(), m.getTitle(), m.getBody());
}
}
💻 Controller
@RestController
@RequestMapping("/api/memos")
public class MemoController {
private final MemoService service;
public MemoController(MemoService service) { this.service = service; }
@PostMapping
public ResponseEntity create(@RequestBody MemoRequest req) {
return ResponseEntity.status(201).body(service.create(req));
}
@GetMapping
public List list() { return service.findAll(); }
@GetMapping("/{id}")
public MemoResponse get(@PathVariable Long id) { return service.findOne(id); }
@PutMapping("/{id}")
public MemoResponse update(@PathVariable Long id, @RequestBody MemoRequest req) {
return service.update(id, req);
}
@DeleteMapping("/{id}")
public ResponseEntity delete(@PathVariable Long id) {
service.delete(id);
return ResponseEntity.noContent().build(); // 204
}
}
💻 검증
# 생성
curl -X POST http://localhost:8080/api/memos
-H "Content-Type: application/json" -d '{"title":"첫 메모","body":"내용"}'
# 목록 / 단건
curl http://localhost:8080/api/memos
curl http://localhost:8080/api/memos/1
# 수정 / 삭제
curl -X PUT http://localhost:8080/api/memos/1
-H "Content-Type: application/json" -d '{"title":"수정","body":"바뀜"}'
curl -X DELETE http://localhost:8080/api/memos/1
💡 팁
- 다음 강의 예외 처리를 적용하면 “없는 id 조회” 시 깔끔한 404 JSON을 돌려줄 수 있습니다.
- 이 API에 4강의 MySQL(Docker)이나 6강의 compose를 연결하면 실제 서비스에 가까워집니다.