Spring Boot

Spring Boot 강좌 05강 — CRUD REST API 완성하기 (실전)

🎯 학습 목표

  • 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를 연결하면 실제 서비스에 가까워집니다.