Self Code Review/(sparta) Spring 기초 - CRUD API

[Spring] Spring 구조와 API 흐름 이해하기2 (스파르타코딩클럽 - 웹개발의 봄, Spring 3주차)

밍굥잉 2022. 5. 26. 20:54

{ ⭐ 더 정리된 노션 버전 ⭐}

 

 

타임라인 서비스로 Spring 구조 이해

API 설계하기 (CRUD)

fair-cheetah-80d.notion.site



❗API 설계하기 (CRUD)



 

✔️ Repository 만들기

 

  1. domain 패키지 (Repo 영역) 만들기
  2. domain - Memo.java 클래스 생성
package com.sparta.week03.domain;

import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@NoArgsConstructor // 기본생성자를 만듭니다.
@Getter
@Entity // 테이블과 연계됨을 스프링에게 알려줍니다.
public class Memo extends Timestamped { // 생성,수정 시간을 자동으로 만들어줍니다.
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String contents;

    public Memo(String username, String contents) {
        this.username = username;
        this.contents = contents;
    }

    public Memo(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
}
  • lombok을 이용해 기본 생성자를 만드는 @NoArgsConstructor, Getter를 생성해주는 @Getter를 추가
  • 테이블과 이어지는 파일이라는 것을 알려주기 위해 @Entity를 추가
  • 메모는 1) 익명의 작성자 이름 (username), 2) 메모 내용 (contents)로 이루어짐
    • 구별해주는 키 id를 생성하고, 자동으로 증가하도록 *@GeneratedValue*(strategy = *GenerationType*.AUTO) 추가
  • 현재 상태 : (🥶)Timestamped, (😤)MemoRequestDto 가 생성되지 않았기 때문에 에러

3. domain - Timestemped.java 클래스 생성

@MappedSuperclass // Entity가 자동으로 컬럼으로 인식합니다.
@EntityListeners(AuditingEntityListener.class) // 생성/변경 시간을 자동으로 업데이트합니다.
public abstract class Timestamped {

    @CreatedDate
    private LocalDateTime createdAt; //생성시간

    @LastModifiedDate
    private LocalDateTime modifiedAt; //수정시간
}
  • @MappedSuperclass : Timestamped를 상속한 클래스(이 프로젝트에서는 Memo.java)에 자동으로 생성 시간과 수정 시간을 colunm으로 인식하게 함.
  • @EntityListeners : Entity(이 프로젝트에서는 Memo.java)가 변화하는 것을 항상 듣고 있다는 것.
  • LocalDateTime : 시간을 나타내는 자료형
  • abstract 추가해서 추상 클래스로 만들어줘야함.
    • New Timestamped 로 사용할 수 없음,
    • (빵 틀은 있지만 빵 못만들고, 상속이 되어야만 만들 수 있는 클래스다)
  • (🥶) Timestamped 클래스 만들어서 에러 해결!

4. domain - MemoRequestDto.java 클래스 생성

@Getter
public class MemoRequestDto {
    private String username;
    private String contents;
}
  • RequestDto는 계층간 데이터 교환을 위해 필요한 정보(username, contents)를 가지고 다님.
  • private로 설정하고, 가져오기 위해 @Getter 추가
  • (😤) MemoRequestDto 클래스 만들어서 에러 해결!

 

5. domain - MemoRepository.java 인터페이스 생성

public interface MemoRepository extends JpaRepository<Memo, Long> {
    List<Memo> findAllByOrderByModifiedAtDesc();
}
  • (extends) MemoRepository는 미리 작성된 JpaRepository(findAll, delete, findById, sava …)를 / Memo라는 클래스의 id가 Long인 것에 대해서 가져다가 쓸거야
  • findAllByOrderByModifiedAtDesc

 

✔️ Service 만들기 

 

 

  1. service 패키지 생성하기
  2. service - MemoService.java 클래스 생성
@Service
public class MemoService {

    @Transactional
    public Long update(Long id, MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new NullPointerException("아이디가 존재하지 않습니다. ")
        );
        memo.update(requestDto);
        return id;
    }
  • update 시 service 가 필요하기 때문에 update 메소드를 생성해줌.
  • public 반환타입 메소드이름(파라미터) { }
  • id를 가져와서 해당하는 id의 Dto를 변경해줘야하기 때문에 (Long id, MemoRequestDto requestDto)을 파라미터로 설정
  • return 하는 id의 반환타입인 Long

 

1) id로 해당하는 메모 찾기

  • Memo memo = memoRepository.findById(id).orElseThrow(
                    () -> new NullPointerException("아이디가 존재하지 않습니다. ")
            );
    
    • memoRepository의 findId로 id를 기준으로 찾을 건데, 만약에 없으면 오류 발생 시켜줘
      • NullPointerException : 가리킨 게 비어있으면 예외 처리 해줘
      • IllegalArgumentException : Argument(파라미터)가 뭔가 잘못됐으면 예외처리 해줘
    • id로 메모를 찾았거나 오류를 보냈으면 memo에 반환해줘.

2) update 하기

  • memo.update(requestDto);
    return id;
    
    • memo에 requestDto를 update 하고, id 값으로 넘겨줘

 

3. domain - Memo.java 클래스에 update 메소드 추가하기

public void update(MemoRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
  • MemoService에서 requestDto를 전달받기 때문에 update 메소드의 파라미터는 requestDto
  • 변경할 값을 실어나르는 Dto의 값이 Memo 안에 저장됨.

 

4. MemoService.java 완성시키기

@RequiredArgsConstructor
@Service
public classMemoService{

    private final MemoRepository memoRepository;

		@Transactional
		publicLongupdate(Longid,MemoRequestDtorequestDto) {
		Memomemo = memoRepository.findById(id).orElseThrow(
		                () -> new NullPointerException("아이디가 존재하지 않습니다. ")
		        );
		        memo.update(requestDto);
		        returnid;
		    }
}

  • memoRepository 꼭 필요하니까 final 적어주기
  • @RequiredArgsConstructor : fianl이 있으면 알아서 생성자를 만들어주는 권한을 줘야함
  • @Service : 서비스라고 알려주기
  • @Transactional : 업데이트 할 때 DB에 진짜 반영되야한다고 알려줌.

 

✔️ Controller 만들기

 

 

  1. Controller 패키지 생성하기
  2. MemoController.java 클래스 생성
@RequiredArgsConstructor
@RestController
public classMemoController{
    private final MemoRepository memoRepository;
    private final MemoService memoService;
}
  • MemoRepository랑 memoService 사용하건데 꼭 필요하니까 final 추가해줘
  • 원래는 요청이 들어오면 new memoController ~ 라고 생성이 된 후 사용해야하는데, @RequiredArgsConstructor, @RestController 를 사용하면 스프링이 그 역할을 해줌.

 

POST - 생성

@PostMapping("/api/memos")
    public Memo createMemo(@RequestBody MemoRequestDto requestDto) {
        Memo memo = new Memo(requestDto);
        return memoRepository.save(memo);
    }
  • requestDto라는 틀에 새로운 메모를 만들어서 memoRepository의 save를 활용해서 저장.

 

Get - 조회

@GetMapping("/api/memos")
    public List<Memo> readMemo() {
        return memoRepository.findAllByOrderByModifiedAtDesc();
    }
  • memoRepository에 있는 모든 데이터를 수정날짜 기준으로 최신순으로 정렬해 보여줌.

 

PUT - 변경

@PutMapping("/api/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        memoService.update(id, requestDto);
        return id;
    }
  • 변경할 메모의 id를 받아서 memoService로 업데이트 하고 다시 id를 돌려줌

 

DELETE - 삭제

@DeleteMapping("/api/memo/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        memoRepository.deleteById(id);
        return id;
    }
  • 삭제할 메모의 id를 받아서 삭제하고 id를 돌려줌.