java/spring

Memo api 구현

일상코더 2022. 10. 7. 03:43

최대한 lombok 없이 구현해 보았다.

 

Memo.java

@Entity
public class Memo extends Timestamped{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String username;

    @Column(nullable = false)
    private String contents;

    // 파라미터가 있는 생성자를 하나라도 정의하게 되면
    // 더이상 기본생성자를 생성하지 않기때문에 기본생성자를 꼭 만들어줘야한다.
    // @NoArgsConstructor 을 사용하는 이유
    public Memo(){
        
    }
    public Memo(MemoRequestDto requestDto){
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }
    //getter를 만들지 않으면 클라이언트에게 데이터를 전달할수 없다.
    //@Gettor lombok을 사용하는 이유
    public Long getId(){
        return this.id;
    }
    public String getUsername(){
        return this.username;
    }
    public String getContents(){
        return this.contents;
    }
    public void update(MemoRequestDto requestDto){
        this.username = requestDto.getUsername();
        this.contents = requestDto.getContents();
    }

}

 

Timestamped.java

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;


@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class Timestamped {
    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime modifiedAt;

    public LocalDateTime getCreatedAt(){
        return this.createdAt;
    }
    public LocalDateTime getModifiedAt(){
        return this.modifiedAt;
    }
}

 

MemoApplication.java

 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing  //생성하지 않으면 생성 / 수정 일자가 db에 들어가지 않는다.
@SpringBootApplication
public class MemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(MemoApplication.class, args);
    }

}

 

MemoRepository.java

 

import com.sparta.memo.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemoRepository extends JpaRepository<Memo, Long> {
		
        //인터페이스로 구현하는 이유
        //실제로 메서드를 정의만 하고 구체적으로 구현하지 않기때문
}

 

MemoController.java

 

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.dto.MemoUsernameResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import com.sparta.memo.service.MemoService;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController //@ResponseBody @Controller를 포함하고있다
		//@RestController를 입힌 @RequestMapping메서드(ex @GetMapping)가
                //기본적을 @ResponseBody 의미 체계를 띠고 있고있는 컨트롤러로 처리한다.
public class MemoController {

    //memoRepository, memoService 값이 변할수 없음을 나타냄
    //final로 처리하게 되면 파라미터 생성자를 만들어줘야한다.
    //@RequiredConstructor을 사용하는 이유
   
    public final MemoRepository memoRepository; 
    public final MemoService memoService;	
	
    //MemoController생성자
    public MemoController(MemoRepository memoRepository, MemoService memoService) {
        this.memoRepository = memoRepository;
        this.memoService = memoService;
    }
	
    //전체 메모 조회
    @GetMapping("/api/memos")
    public List<MemoResponseDto> getMemos(){
        return memoRepository.findAll().stream()
                .map(MemoResponseDto::new)
                .collect(Collectors.toList());
    }
    //id로 user이름 조회
    @GetMapping("/api/memos/user/{id}")
    public List<MemoUsernameResponseDto> getUsername(@PathVariable Long id){
        return memoRepository.findById(id).stream()
                .map(MemoUsernameResponseDto::new)
                .collect(Collectors.toList());
    }
	//id로 조회
    @GetMapping("/api/memos/{id}")
    public List<MemoResponseDto> getMemo(@PathVariable Long id){
        return memoRepository.findById(id).stream()
                .map(MemoResponseDto::new)
                .collect(Collectors.toList());
    }
	//메모 등록
    @PostMapping("/api/memos")
    public MemoResponseDto createMemos(@RequestBody MemoRequestDto requestDto){
        Memo memo = memoRepository.save(new Memo(requestDto));
        return new MemoResponseDto(memo);
    }
	//id로 메모 수정
    @PutMapping("/api/memos/{id}")
    public Long update(@PathVariable Long id, @RequestBody MemoRequestDto requestDto){
        return memoService.update(id, requestDto);
    }
	//id로 메모 삭제
    @DeleteMapping("/api/memos/{id}")
    public Long Delete(@PathVariable Long id){
        memoRepository.deleteById(id);
        return new MemoResponseDto().getId();
    }

}

 

MemoRequestDto.java

 


// 클라이언트에서 메모를 등록할때 @RequestBody로 json형식의 데이터가 넘어오면
// MemoRequestDto를 통해 Entity테이블에 값이 변경되고 MemoResponseDto를 이용해서
// DB에서 받은 데이터를 client에 보내준다.
public class MemoRequestDto {
    private String username;
    private String contents;

    public String getUsername(){
        return this.username;
    }
    public String getContents(){
        return this.contents;
    }
}

 

MemoResponseDto.java

 

public class MemoResponseDto {
    private Long id;
    private String username;
    private String contents;
    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;

	
    //Memo entity 테이블에 있는 데이터들이 entity에서 바로 클라이언트에게 가는게아니라
    //MemoResponseDto를 통해 전달된다. entity는 db와 가장 근접해있기 때문에 
    //entity를 직접 수정하는것보다 ResponseDto를 사용해서 넘겨주는것이 안정이나
    //유지보수 측면에서 더 좋다.
    public MemoResponseDto(Memo memo){
        this.id = memo.getId();
        this.username = memo.getUsername();
        this.contents = memo.getContents();
        this.createdAt = memo.getCreatedAt();
        this.modifiedAt = memo.getModifiedAt();
    }
    
    //위에있는 파라미터가 있는 생성자가 있으면
    //컴파일할때 자동으로 기본생성자가 생성되지 않기때문에
    //기본생성자를 만들어줘야한다
    //@NoArgsConstructor을 사용하는이유 
    public MemoResponseDto(){

    }
    
    //값이 클라이언트로 전달되어야기때문에 getter를 만들어줘야함
    //@Getter를 사용하는 이유
    public Long getId(){
        return this.id;
    }
    public String getUsername(){
        return this.username;
    }
    public String getContents(){
        return this.contents;
    }
    public LocalDateTime getCreatedAt(){
        return this.createdAt;
    }
    public LocalDateTime getModifiedAt(){
        return this.modifiedAt;
    }
}

 

MemoUsernameResponseDto.java

 

public class MemoUsernameResponseDto {
    private String username;

    public MemoUsernameResponseDto(Memo memo){
        this.username = memo.getUsername();
    }

    public String getUsername(){
        return this.username;
    }
}

 

MemoService.java

import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

@Service
public class MemoService {

    private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository){
        this.memoRepository = memoRepository;
    }
	//현재 MemoController에서 구현된 모든 로직들은 아래와 같이 
    //service단에서 구현되어야한다.
    //일부러 refacoring을 위해 MemoController에 구현했다.
    @Transactional
    public Long update(Long id, @RequestBody MemoRequestDto requestDto){
        Memo memo = memoRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당아이디가 없습니다.")
        );
        memo.update(requestDto);
        return new MemoResponseDto(memo).getId();
    }
}

 

Lombok 없이 구현해서 알게된 오류

 

1. Memo Entity에 기본생성자가 없으면 컴파일시 오류가 난다.

        - 매개변수가 있는 생성자를 만들면 컴파일러가 기본생성자를 자동으로 만들어주지 않기때문에 따로 만들어줘야함

        - @NoArgsConstructor을 사용하는 이유

 

2. getter를 만들지 않으면 데이터가 클라이언트에게 전달이 되지 않는다

       - @Getter을 사용하는 이유

 

3. final 상수 선언시 생성자를 무조건 생성해줘야한다.

       - @RequiredArgsConstructor을 사용한이유

 

 

https://github.com/Liam-Genius/homework2/tree/master/spring%201st%20week/post01/memo

 

GitHub - Liam-Genius/homework2

Contribute to Liam-Genius/homework2 development by creating an account on GitHub.

github.com