끝을 보는 용기

Spring 본캠프 Day 057 - 기초 Spring 4주차 및 5주차 완강

writingforever162 2024. 12. 2. 23:18

"과제 하기 전부터 트러블(Trouble) 일으키는 사람?"

 

"저요!"

 

오늘 사전캠프 기간부터 우수 TIL에 선정된 수강생 전원에게 5,000포인트씩 지급되었다. 주머니가 두둑해져서 기분이 좋기도 했지만, '필수로 구현해야 하는 과제를 전부 무사히 제출하면 꼭 우주 비행기랑 플라밍고 튜브 사야지!' 같은 의욕이 샘솟아서 포인트를 쓰기가 망설여졌다. 강의 5주 차부터 직접 메모장을 만드는 실습이 있어서 '모르지만 일단 따라 친다'는 마음으로 코딩했는데, 별의별 트러블(Trouble)을 일으키거나 머리에 쥐가 날 때 '꼭 사고 만다'는 생각으로 5주 차 학습을 마쳤다.

 

주말에 실습할 부분을 무작정 따라 작성하며 눈에 보이는 족족 다시 강의 자료에서 해당 용어를 찾아 공부했는데, 효과가 있긴 한 듯했다. 어제보다는 한 발짝 Spring과 가까워진 느낌이 들었다. 아직 손잡을 정도로 가까워지려면 까마득하긴 하지만.

어제 생성한 저장소는 조금 아쉽긴 했지만, 주석으로 필기한 내용은 모두 익혔다는 생각에 지우고 새로운 저장소를 생성했다. 원래 계획은 6주 차 강의까지 전부 듣기였으나, 5주 차 실습을 낑낑대면서 했더니 12시간 학습을 훌쩍 넘겨서 내일로 미뤄야 한다. 대신 내일 오전 중에 6주 차 강의를 모두 듣고 필수 과제 0단계부터 차근차근히 할 예정이다. '일단 박치기!' 전략에 가깝지만 우선 '차근차근히'라고 표현하련다.

 

깃허브(GitHub)에 올린 실습 내용을 복사해서 TIL에 붙여넣기로 하루를 마치련다. 아침 기상에 성공해야 거북목 스트레칭과 허리 근력 강화 운동으로 하루를 열 수 있다. 오늘 작성한 TIL은 과제를 할 때 정말 많이 참고할 듯하다.

 

1. 메모를 CRUD할 수 있는 Web Application 

(1) 요구사항 

- 통신 데이터 형태: JSON

- 각각의 메모 구성: 식별자(id), 제목(title), 내용(contents) 

(2) 실습 결과

package com.example.memo.entity;
// [1/4] entity 패키지에 있는 Memo 클래스
import com.example.memo.dto.MemoRequestDto;
import com.example.memo.dto.MemoResponseDto;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class Memo {
    private Long id;
    private String title;
    private String contents;

    public void update(MemoRequestDto dto) {
        this.title = dto.getTitle();
        this.contents = dto.getTitle();
        // 매개변수인 MemoResponseDto dto == 요청 정보
    }
}
package com.example.memo.dto;
// [2/4] dto 패키지에 있는 MemoRequestDto 클래스
import lombok.Getter;

@Getter
public class MemoRequestDto {
    // id는 서버에서 관리하기 때문에 요청받지 않는다.
    private String title;
    private String contents;
}
package com.example.memo.dto;
// [3/4] dto 패키지에 있는 MemoResponseDto 클래스
import com.example.memo.entity.Memo;
import lombok.Getter;

@Getter
public class MemoResponseDto {
    private Long id;
    private String title;
    private String contents;

    // MemoController 작성이 끝나면 생성자 만들기
    public MemoResponseDto(Memo memo) {
        this.id = memo.getId();
        this.title = memo.getTitle();
        this.contents = memo.getContents();
        /*
         memo 객체가 그대로 반횐되는 게 아니다.
         MemoResponseDto 형태로 바뀌어서 응답되어야 한다.
         */
    }
}
package com.example.memo.controller;
// [4/4] controller 패키지에 있는 MemoController 클래스

/*
[절대 잊으면 안 되는 것]
첫째, src>test>java에 클래스를 넣지 않았는지 확인하자
     ➡️src>main>java이다. 그렇게 안 하고 postman을 실행하면 404 오류 뜬다.
둘째, import가 두 번 되지 않았는지 꼭 살펴보자.
 */

import com.example.memo.dto.MemoRequestDto;
import com.example.memo.dto.MemoResponseDto;
import com.example.memo.entity.Memo;
import org.springframework.web.bind.annotation.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/memos")
/*
[@RequestMapping]
- @PostMapping 사용 후 @RequestMapping을 추가하자.
- prefix하는 URL을 설정할 때 사용한다.
  == prefix처럼 하위 메서드 전체에 영향을 주기 때문에 'prefix'라고 표현함
- 공통적으로 들어가는 /memos라는 URL을 적어둔다.
 */

public class MemoController {
    private final Map<Long, Memo> memoList = new HashMap<>();
    // key 값 = Long

    // ------------------------- Create -------------------------
    @PostMapping
    /*
    - 생성이기 때문에 @PostMapping을 사용한다.
    Q. @PostMapping("/healthy") 이렇게 적으면?
    A. == localhost:8080/memos/healthy
       영향 범위는 메서드의 블럭만 해당한다.
       Q. 만약 다른 메서드에 영향을 주려면? 
       A. 해당 메서드 위에 다시 annotation을 써야 한다.
     */
     
    /*
    [해야 할 일]
    [1] 식별자가 1씩 증가해야 함. 중복이 되면 안 되니까!
    [2] 요청받은 데이터로 Memo를 생성해야 함
    [3] Inmemory DB에 메모 저장 
        == 데이터베이스 대신 Java의 Map 자료구조를 이용한다.
        == 자료구조라서 프로젝트를 종료하면 그 안의 모든 데이터가 지워진다.
           이런 이유로 Inmemory라고 표현한다.
     */ 
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto dto) {
        Long memoId = memoList.isEmpty() ? 1 : Collections.max(memoList.keySet()) + 1;
        /*
        [1] 식별자가 1씩 증가하도록 삼항연산자를 사용해서 구현하기
        memoList.isEmpty()
        ▶ 비어 있는지 검사
        물음표 뒤에 있는 1
        ▶ 비어 있다면 1로 초기 값 설정
        Collections.max(memoList.keySet())
        ▶ memoList.keySet() 안에 든 값 중 최댓값을 뽑아내는 메서드
        memoList.keySet()
        ▶ memoList 안에 있는 key 값을 전부 뽑아내는 메서드
           == 모든 Long 값을 꺼내서 최댓값을 반환해주는 메서드
           [설명] 우리는 1씩 증가해야 하므로 + 1 추가
         */

        Memo memo = new Memo(memoId, dto.getTitle(), dto.getContents());
        /*
        [2] 요청받은 데이터로 Memo 객체 생성하기
        - MemoRequestDto 형태로 요청받았으므로 Memo 객체로 바꿔줘야 한다.
        - 첫 번째 인자로 memoId 값 전달
        - 두 번째로 title을, 세 번째로 contents를 전달해 줘야 한다.
          이때, 두 데이터 모두 dto 안에 있으므로 getter 사용
        - 그다음 Memo 타입 변수로 받아주면 된다.
         */

        memoList.put(memoId, memo);
        /*
        [3] 실제 데이터베이스 (현재는 자료구조) 안에 저장하기
        key 값은 memoId
        저장될 객체 형태는 memo
         */

        return new MemoResponseDto(memo);
        /*
        [4] 저장된 데이터를 MemoResponseDto 형태로 반환하기
        이후 MemoResponseDto 클래스로 돌아가서 생성자를 만들어준다.
        return new MemoResponseDto();
        그다음, 위와 같이 적은 곳의 () 안에 memo를 넣어준다.
         */
    }

    // -------------------------- Read --------------------------
    @GetMapping("/{id}")
    // 이미 prefix로 /memos를 적었으므로 식별자를 추가로 적는다.
    public MemoResponseDto findMemoById(@PathVariable Long id) {
        // 식별자를 매개변수로 바인딩할 때는 @PathVariable을 사용한다.
        Memo memo = memoList.get(id);

        return new MemoResponseDto(memo);
    }

    // ------------------------- Update -------------------------
    @PutMapping("/{id}")
    /*
    [1] 전체 수정이므로 PutMapping이다.
    [2] 단건을 수정해야 하므로 아까처럼 경로 변수가 필요하다.
    [3] id값 뿐만 아니라 우리가 수정하려는 데이터도 요청받아야 한다.
        ➡️ RequestDto 형태로 받기로 한 점을 기억하자!
        ➡️ 지금은 title과 contents 둘 다 수정할 수 있다고 가정했다.
            == 아래에 @RequestBody MemoRequestDto dto를 작성한 이유
     */
     
    public MemoResponseDto updateMemoById(@PathVariable Long id, @RequestBody MemoRequestDto dto) {
        Memo memo = memoList.get(id);
        /*
        - 수정하려면 저장된 메모부터 꺼내와야 하므로 위와 같이 작성한다.
        - 이후 Memo 클래스로 돌아가서 update() 메서드를 새로 만든다.
         */

        memo.update(dto);
        return new MemoResponseDto(memo);
    }

    // ------------------------- Delete -------------------------
    @DeleteMapping("/{id}")
    public void deleteMemo (@PathVariable Long id) {
        // 삭제라서 따로 반환할 값이 없으므로 void로 설정
        memoList.remove(id);
    }
}

 

2. 1에서 만든 Web Appilcation 수정하기깃허브(Github) 링크