메모장 CRUD 개선

하마·2025년 3월 21일

Spring

목록 보기
17/22
post-thumbnail

1. 요구사항 분석 & 설계


1. API 요구사항


  1. 통신 데이터 형태는 JSON이다.
  2. 각각의 메모는 식별자(id), 제목(title), 내용(contents)으로 구성되어 있다.
  1. 응답을 각각의 API에 알맞게 해야 한다.
  2. 메모를 생성할 수 있다. (CREATE)
    • 메모 생성 시 제목, 내용이 필요하다.
    • 생성된 데이터(식별자, 제목, 내용)가 응답된다.
  3. 메모 전체 목록을 조회할 수 있다. (READ)
    • 여러 개의 데이터를 배열 형태로 한번에 응답한다.
    • 데이터가 없는 경우 비어있는 배열 형태로 응답한다.
  4. 메모 하나를 조회할 수 있다. (READ)
    • 조회할 memo에 대한 식별자 id값이 필요하다.
    • 조회된 데이터가 응답된다.
    • 조회될 데이터가 없는 경우 Exception이 발생한다.
  5. 메모 하나를 전체 수정(덮어쓰기)할 수 있다. (UPDATE)
    • 수정할 memo에 대한 식별자 id값이 필요하다.
    • 수정할 요청 데이터(제목, 내용)가 꼭 필요하다.
    • 수정된 데이터가 응답된다.
    • 수정될 데이터가 없는 경우 Exception이 발생한다.
  6. 메모 하나의 일부를 수정할 수 있다. (UPDATE)
    • 수정할 memo에 대한 식별자 id값이 필요하다.
    • 수정할 요청 데이터(제목)가 꼭 필요하다.
    • 수정된 데이터가 응답된다.
    • 수정될 데이터가 없는 경우 Exception이 발생한다.
  7. 메모를 삭제할 수 있다. (DELETE)
    • 삭제할 memo에 대한 식별자 id값이 필요하다.
    • 삭제될 데이터가 없는 경우 Exception이 발생한다.

2. HTTP API 설계


기능MethodURLRequestResponse
메모 생성POST/api/memos{
 "title": string,
 "content": string
}
성공 시 201 Created
{
 "id": Long,
 "title": string,
 "content": string
}
메모 전체 조회GET/api/memos1. 성공 시 200 OK
[
 {
  "id": Long,
  "title": string,
  "content": string
 },
 ...
]
2. 없으면 200 OK와 비어있는 배열 응답
[]
메모 단건 조회GET/api/memos/{id}1. 성공 시 200 OK
{
 "id": Long,
 "title": string,
 "content": string
}
2. 실패 시 404 Not Found
해당 식별자의 메모가 존재하지 않는 경우
메모 수정
(덮어쓰기)
PUT/api/memos/{id}{
 "title": string,
 "content": string
}
1. 성공 시 200 OK
{
 "id": Long,
 "title": string,
 "content": string
}
2. 실패 시
- 404 Not Found
해당 식별자의 메모가 존재하지 않는 경우
- 400 Bad Requset
필수 값이 없는 경우
메모 제목 수정PATCH/api/memos/{id}{
 "title": string
}
1. 성공 시 200 OK
{
 "id": Long,
 "title": string,
 "content": string
}
2. 실패 시
- 404 Not Found
해당 식별자의 메모가 존재하지 않는 경우
- 400 Bad Requset
필수 값이 없는 경우
메모 삭제DELETE/api/memos/{id}1. 성공 시 200 OK
2. 실패 시 404 Not Found
해당 식별자의 메모가 존재하지 않는 경우

2. API 구현


1. 메모 생성 API


  1. 요구사항

    • 메모를 생성할 수 있다.
      • 메모 생성 시 제목, 내용이 필요하다.
    • 생성된 데이터(식별자, 제목, 내용)가 응답된다.
      • 응답 상태 코드: 201 CREATED
  2. API 구현

MemoController - createMemo() 수정

@RestController
@RequestMapping("/memos")
public class MemoController {

  private final Map<Long, Memo> memoList = new HashMap<>();

  @PostMapping
  public ResponseEntity<MemoResponseDto> createMemo(@RequestBody MemoRequestDto dto) {

    Long memoId = memoList.isEmpty() ? 1 : Collections.max(memoList.keySet()) + 1;
    Memo memo = new Memo(memoId, dto.getTitle(), dto.getContents());

    memoList.put(memoId, memo);

    return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.CREATED);

  }
  
}

ResponseEntity<> : 상태 코드를 같이 반환하기 위해 사용
ResponseEntity<> 사용 시 모든 응답을 ResponseEntity 로 통일하는 게 더욱 좋음

2. 메모 전체 조회 API


  1. 요구사항

    • 메모 전체 목록을 조회할 수 있다.
      • 여러 개의 데이터를 배열 형태로 한 번에 응답한다.
    • 데이터가 없는 경우 비어있는 배열로 응답한다.
      • 응답 상태 코드: 200 OK
  2. API 구현

MemoController - findAllMemos() 생성

@RestController
@RequestMapping("/memos")
public class MemoController {

  private final Map<Long, Memo> memoList = new HashMap<>();

  @GetMapping
  public List<MemoResponseDto> findAllMemos() {

    // 리스트 초기화
    List<MemoResponseDto> responseList = new ArrayList<>();

    // MashMap<Memo> -> 전체 조회 -> List<MemoResponseDto>
    // 1. 반복문 사용
    for (Memo memo : memoList.values()) {
      MemoResponseDto responseDto = new MemoResponseDto(memo);
      responseList.add(responseDto);
    }

    // 2. 스트림 사용
//    responseList = memoList.values().stream()
//                                    .map(MemoResponseDto::new)
//                                    .toList();

    return responseList;

  }
  
}

@GetMapping : 아무것도 없으면 @RequestMapping 의 URL이 Mapping됨

3. 메모 단건 조회 API


  1. 요구사항

    • 메모 전체 목록 중 하나를 조회할 수 있다.
    • 조회할 메모에 대한 식별자가 필요하다.
    • 식별자에 따라 조회된 데이터가 응답된다.
      • 응답 상태 코드: 200 OK
    • 조회될 데이터가 없는 경우 예외가 발생한다.
      • 응답 상태 코드: 404 NOT FOUND
  2. API 구현

MemoController - findMemoById() 수정

@RestController
@RequestMapping("/memos")
public class MemoController {

  private final Map<Long, Memo> memoList = new HashMap<>();

  @GetMapping("/{id}")
  public ResponseEntity<MemoResponseDto> findMemoById(@PathVariable Long id) {

    Memo memo = memoList.get(id);

    // 해당 식별자를 가진 메모가 없으면 404 NOT FOUND 반환
    if (memo == null) {
      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.OK);

  }
  
}

4. 메모 단건 전체 수정 API


  1. 요구사항

    • 메모 하나를 전체 수정할 수 있다. (덮어쓰기)
      • 수정할 메모에 대한 식별자가 필요하다.
    • 수정할 요청 데이터(제목, 내용)가 필요하다.
    • 식별자에 따라 수정된 데이터가 응답된다.
      • 응답 상태 코드: 200 OK
    • 수정될 데이터가 없는 경우 예외가 발생한다.
      • 응답 상태 코드: 404 NOT FOUND
  2. API 구현

MemoController - updateMemoById() 수정

@RestController
@RequestMapping("/memos")
public class MemoController {

  private final Map<Long, Memo> memoList = new HashMap<>();

  @PutMapping("/{id}")
  public ResponseEntity<MemoResponseDto> updateMemoById(
      @PathVariable Long id,
      @RequestBody MemoRequestDto dto
  ) {
    Memo memo = memoList.get(id);

    // 해당 식별자를 가진 메모가 없으면 404 NOT FOUND 반환
    if (memo == null) {
      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    // 제목이나 내용이 없으면 400 BAD REQUEST 반환
    if (dto.getTitle() == null || dto.getContents() == null) {
      return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }

    memo.update(dto);

    return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.OK);

  }
  
}

5. 메모 단건 일부 수정 (제목) API


  1. 요구사항

    • 메모 하나를 일부만 수정할 수 있다.
    • 수정할 메모에 대한 식별자가 필요하다.
    • 수정할 요청 데이터(제목)가 필요하다.
      • 응답 상태 코드: 400 BAD REQUEST
    • 식별자에 따라 수정된 데이터가 응답된다.
      • 응답 상태 코드: 200 OK
    • 수정될 데이터가 없는 경우 예외가 발생한다.
      • 응답 상태 코드: 404 NOT FOUND
  2. API 구현

MemoController - updateTitle() 생성

@RestController
@RequestMapping("/memos")
public class MemoController {

  private final Map<Long, Memo> memoList = new HashMap<>();

  // 일부 수정이기 때문에 @PatchMapping 사용
  @PatchMapping("/{id}")
  public ResponseEntity<MemoResponseDto> updateTitle(
      @PathVariable Long id,
      @RequestBody MemoRequestDto dto
  ) {

    Memo memo = memoList.get(id);

    // 해당 식별자를 가진 메모가 없으면 404 NOT FOUND 반환
    if (memo == null) {
      return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

    // 내용이 있거나, 제목이 null이면 400 BAD REQUEST 반환
    if (dto.getTitle() == null || dto.getContents() != null) {
      return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }

    memo.updateTitle(dto);

    return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.OK);

  }
  
}

6. 메모 삭제 API


  1. 요구사항
    • 메모를 삭제할 수 있다.
    • 삭제할 메모에 대한 식별자가 필요하다.
    • 응답 데이터는 없어도 된다.
      • 응답 상태 코드: 200 OK
    • 삭제될 데이터가 없는 경우 예외가 발생한다.
      • 응답 상태 코드: 404 NOT FOUND
  2. API 구현

MemoController - deleteMemo() 수정

@RestController
@RequestMapping("/memos")
public class MemoController {

  private final Map<Long, Memo> memoList = new HashMap<>();

  @DeleteMapping("/{id}")
  public ResponseEntity<Void> deleteMemo(@PathVariable Long id) {

    // memoList의 key가 id를 포함하고 있다면 삭제
    if (memoList.containsKey(id)) {
      memoList.remove(id);

      return new ResponseEntity<>(HttpStatus.OK);
    }

    return new ResponseEntity<>(HttpStatus.NOT_FOUND);

  }
  
}

7. GitHub


3. 해결한 문제점


  • 응답 코드를 세분화했다.
  • 적절한 예외 처리로 HTTP Status를 반환했다.

4. 해결하지 못한 문제점


  • 서버가 종료된 후 다시 켜지면 데이터가 모두 초기화 된다.
  • Controller에 책임이 너무 많다.
    • 요청, 비지니스 로직, 응답, 예외 처리 등

참고자료


스프링 입문 - 5주차

  • 메모장 프로젝트

0개의 댓글