인텔리제이에서 new project

Generators를 Spring Boot로 설정하고
Language는 Java, Gradle-Groovy
JDK는 Amazon Corretto로 설정후 next!

Dependency에 Spring web, Lombok, Thymleaf 추가하기
:항상 프로젝트 시작 전에 요구사항을 정의하는 것이 중요하다
식별자(id), 제목(title), 내용(contents)으로 구성| 생성 | 조회 | 수정 | 삭제 | |
|---|---|---|---|---|
| CRUD | POST | GET | PUT | DELETE |
| URL | /memos | /memos/{id} | /memos/{id} | /memos/{id} |
import lombok.Getter;
@Getter
@AllArgsConstructor
public class Memo {
private Long id;
private String title;
private String contents;
}
Memo 생성자를 만들고, @Getter 어노테이션으로 게터 자동으로 만들어주기
Long타입은 null을 받을 수 있으므로 Long으로 id 설정
@AllArgsConstructor
: 모든 필드를 초기화하는 생성자를 자동으로 만들어 줌
this.~ 이런거 안해도 알아서 해주는거!
: 요청 데이터를 처리하는 객체는 보통 RequestDto를 사용한다.
import lombok.Getter;
@Getter
public class MemoRequestDto {
private String title;
private String contents;
}
요청 데이터는 title과 contents로 구성하자
: Client에 데이터를 반환할 때 사용
import lombok.Getter;
@Getter
public class MemoResponseDto {
private Long id;
private String title;
private String contents;
}
이제 메모를 제어하는 Controller 클래스를 만들어보자
아까 정의했던대로, 생성/읽기/수정/삭제 기능이 되어야한다.
메모 데이터를 메모리에 저장할 memoList를 Map으로 만들자
private final Map<Long, Memo> memoList = new HashMap<>();
(1) 메모 생성 PostMapping
@RestController
@RequestMapping("/memos") // prefix
public class MemoController {
// 자료구조가 DB 역할 수행
private final Map<Long, Memo> memoList = new HashMap<>();
@PostMapping
public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
// 식별자가 1씩 증가 하도록 만듦
Long memoId = memoList.isEmpty() ? 1 : Collections.max(memoList.keySet()) + 1;
// 요청받은 데이터로 Memo 객체 생성
Memo memo = new Memo(memoId, requestDto.getTitle(), requestDto.getContents());
// Inmemory DB에 Memo 저장
memoList.put(memoId, memo);
return new MemoResponseDto(memo);
}
}
(2) 메모 조회 GetMapping
@GetMapping("/{id}")
public MemoResponseDto findMemoById(@PathVariable Long id) {
Memo memo = memoList.get(id);
return new MemoResponseDto(memo);
}
(3) 메모 수정 PutMapping
일치하는 id를 조회한 후 그 아이디에 맞는 메모 update메서드를 활용해 수정한다.
Memo 클래스에 업데이트 메서드 추가 - 요청 dto를 활용해서
public void update(MemoRequestDto requestDto) {
this.title = requestDto.getTitle();
this.contents = requestDto.getContents();
}
update(변경할 값 넣기) 하면 업데이트가 되겠죠
@PutMapping("/{id}") // URL 경로에 {id} 라는 값을 포함
public MemoResponseDto updateMemoById(
@PathVariable Long id, // URL 경로의 id 값을 받아옴
@RequestBody MemoRequestDto dto) { // 요청 바디의 JSON 데이터를 받아옴
Memo memo = memoList.get(id); // 받아온 id로 Memo 객체 조회
memo.update(dto); // Memo 객체를 수정
return new MemoResponseDto(memo); // 수정된 Memo 객체를 응답으로 보냄
}
여기서, @PathVariable 이란?
: URL 경로(Path)에 포함된 값을 변수로 받아오는 어노테이션
→ URL 자체에 포함된 값을 메서드의 파라미터로 사용하는 방법!
🔍 사용법
URL 경로에서 {} 중괄호로 감싸서 경로 정의
(변수처럼 값 받아오기)
@PutMapping("/memos/{id}")
{id}는 경로의 일부로 사용되지만, 이 값은 동적으로 변경될 수 있음
예를 들어, 클라이언트가 요청을 보낼 때:
PUT /memos/3
여기서 3이라는 값이 @PathVariable Long id로 전달
원하는 id를 골라서 수정할 수 있겠죠??
그럼 이제 비슷한 원리로 DELETE도 구현해보자
(4) 메모 삭제 DeleteMapping
@DeleteMapping("/{id}")
public void deleteMemo(
@PathVariable Long id
) {
memoList.remove(id);
}
마찬가지로 id로 삭제할 메모를 찾고, 리스트에서 삭제한다
1번 메모를 삭제하고 다시 1번 메모를 조회하게 되면 아래처럼 500번대 에러가 생기는 것을 볼 수 있다! (NullPointerExecption이 발생)

ResponseEntity<>원래는 MemoResponseDto 객체를 직접 반환하고 있었다.
→ Spring은 자동으로 HTTP 상태 코드 200 OK를 반환한다
➡️ 성공했을 때는 괜찮지만, 다양한 상황에 따라 응답 상태 코드를 세밀하게 설정할 수 없다는 문제가 있다ㅜㅜ
✅ ResponseEntity<MemoResponseDto>로 변경!
//원래 코드
@PostMapping
public MemoResponseDto createMemo(@RequestBody MemoRequestDto dto){
...생략
return new MemoResponseDto(memo);
//개선 코드
@PostMapping
public ResponseEntity<MemoResponseDto> createMemo(@RequestBody MemoRequestDto dto){
...생략
return new ResponseEntity<>(new MemoResponseDto(memo), HttpStatus.CREATED);
원래 MemoResponseDto로 받던걸 ResponseEntity<>로 변경했다
그리고 반환값도 지정해주어야하므로 HttpStatus.CREATED를 붙여서 상태코드를 명시할 수 있게 해줬다

그러면 이렇게 201 Created라고 명시되는 모습을 볼 수 있다!
👍 장점
1. 클라이언트에게 더 의미 있는 응답 상태 코드를 전달
2. 단순히 데이터를 반환하는 것뿐만 아니라, 요청의 성공/실패를 명확하게 나타낼 수 있다
3. 응답 헤더를 추가 또는 설정 가능하다
4. 에러처리 및 다양한 응답 처리의 일관성을 제공한다
➡️ 즉, API 설계 원칙에 따라 더 의미 있는 응답 상태 코드와 유연한 설정을 제공한다!!
메모 내용 저장을 이렇게 해쉬맵으로 저장했었다.
private final Map<Long, Memo> memoList = new HashMap<>();
메모를 조회할때 한번에 다 조회하고 싶어서 전체 조회할 때 쓸 수 있는 리스트를 만들어보려고 한다.
근데 저장이 해쉬맵으로 되어있으니까 리스트로 넘기려면 어떤 과정이 필요하다!
먼저 응답할 리스트를 만들어주자.
MemoResponseDto 형식으로 리스트를 만들자
List<MemoResponseDto> responseList = new ArrayList<>();
이제 해쉬맵에서 리스트로 변환해보자
memoList 내부에 있는 value들을 꺼내서 다시 넣어주면 된다
for (Memo memo : memoList.values()){
MemoResponseDto responseDto = new MemoResponseDto(memo);
responseList.add(responseDto);
}
스트림을 활용해서 1줄로 이렇게 표현할 수도 있다
responseList = memoList.values().stream().map(MemoResponseDto::new).toList();

이렇게 리스트 형태로 잘 조회되는 것을 확인할 수 있다!