1. 프로젝트 생성 

 

 

 

 


2. 스프링부트 버전과 dependency 추가

 

 

 


3. 빌드가 성공하면 먼저 src > main > resources > application.properties(스프링 환경 설정 파일)에 h2 설정 추가

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:db;MODE=MYSQL;
spring.datasource.username=sa
spring.datasource.password=

 

 

 

 


4. 스프링 구조에 맞게 패키지를 나눠서 5개 생성

 

 

 

 

 


5. DB로 생성할 Entity Class 작성

 

① 메모 Entity (작성한 글들이 저장될) : Class

package com.example.memopractice.entity;

import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter             // Class 모든 필드의 Getter method를 생성
@Entity             // Entity임을 선언
@NoArgsConstructor  // @NoArgsConstructor : 파라미터가 없는 기본 생성자를 생성
public class Memo extends Timestamped {                 // 시간 값을 가져오기 위해 Timestamped 상속
    @Id                                                 // ID임을 선언
    @GeneratedValue(strategy = GenerationType.AUTO)     // 값 자동 생성 , 생성 전략 : 자동 증감
    private Long id;

    @Column(nullable = false)                           // 컬럼 설정 , null값 허용 선택 : 불가
    private String title;

    @Column(nullable = false)
    private String author;

    @Column(nullable = false)
    private String contents;

    @Column(nullable = false)
    private String password;
}

+ @Entity 어노테이션 위에 Entity값을 받을 수 있게 @Getter와 기본 생성자를 생성해주는 @NoArgsConstructor을 추가

 


 

② Timestamped Entity (작성된 시간, 수정된 시간을 추적할, 공용적인 Entity라 별도로 나누고 상속) : Class

package com.example.memopractice.entity;

import lombok.Getter;
import org.springframework.data.annotation.*;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.time.LocalDateTime;


                                                // @EntityListeners : 삽입, 삭제, 수정, 조회 등의 작업을 할 때 전, 후에 어떠한 작업을 하기 위해 이벤트 처리를 위한 어노테이션
@EntityListeners(AuditingEntityListener.class)  // AuditingEntityListener.class : Audit(감시하다) 옵션은 시간에 대해서 자동으로 값을 넣어 주는 기능
@MappedSuperclass                               // 공통 매핑 정보가 필요할 때, 부모클래스에 선언하고 속성만 상속 받아서 사용 하고 싶을때 사용
@Getter                                         // Class 모든 필드의 Getter method를 생성
public class Timestamped {

    @CreatedDate                        // 생성된 시간 정보
    private LocalDateTime createdAt;

    @LastModifiedDate                   // 수정된 시간 정보
    private LocalDateTime modifiedAt;
}

 

 

 

 

 


6. DB를 생성시켜주고 사용할 메소드를 정의하는 Repository를 생성 : Interface

package com.example.memopractice.repository;

import com.example.memopractice.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MemoRepository extends JpaRepository <Memo, Long>{
}

 

 

 

 


7. 일단 여기까지 작성하고 실행해주면 h2-console을 이용해 테이블이 생성된 걸 확인할 수 있다.

 

 

 

 


8. DTO를 생성한다.

API 명세서를 생각하면서 RequestDto와 ResponseDto, 그리고 Result만 내보낼 ResultDto를 각각 생성

 

① RequestDto : 요청을 받아올 Dto 

package com.example.memopractice.dto;

import lombok.Getter;

@Getter
public class RequestDto {
    private String title;
    private String author;
    private String contents;
    private String password;
}

 


 

② ResponseDto : 응답으로 글 작성 정보를 내려줄 Dto, 생성시 값을 넣을 수 있게 생성자 작성

package com.example.memopractice.dto;

import com.example.memopractice.entity.Memo;
import lombok.Getter;

import java.time.LocalDateTime;

@Getter
public class ResponseDto {
    private Long id;
    private String title;
    private String author;
    private String contents;
    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;

    // password는 Reponse에 노출하지 않는다.


    // Entity -> DTO로 변환 : Entity를 그대로 밖으로 내보내면 안되기 때문에 DTO로 데이터를 필터링하고 필요한 부분만 정리하여 DTO로 내보낸다.
    public ResponseDto(Memo memo){
        this.id = memo.getId();
        this.title = memo.getTitle();
        this.author = memo.getAuthor();
        this.contents = memo.getContents();
        this.createdAt = memo.getCreatedAt();
        this.modifiedAt = memo.getModifiedAt();
    }
}

 


 

③ ResultDto :  응답으로 성공여부를 내려줄 Dto, 생성시 값을 넣을 수 있게 생성자 작성

package com.example.memopractice.dto;

import lombok.Getter;

@Getter
public class ResultDto {
    private boolean success;
    
    public ResultDto(boolean result){ // 외부에서 값을 받아와 적용시키기 위해 매개값 생성자
        this.success = result;
    }
}

 

 

 

 


9. 요청을 받을 Controller를 생성한다.

요청에 따른 URL을 각각 나누고 서비스를 연결해줄 준비를 한다.

URL등 RESTful 하게 만들수 있도록 신경쓰자

package com.example.memopractice.controller;

import com.example.memopractice.dto.*;
import com.example.memopractice.service.MemoService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController // @ResponseBody 어노이테이션을 따로 쓰지 않기 위해 @ResponseBody + @Controller인 @RestController 사용
@RequiredArgsConstructor // final변수나 Notnull 표시가된 변수등 필수적인 정보를 세팅하는 생성자를 생성 어노테티션
public class MemoController {

    // Controller은 Client에 가장 맞닿아 있어 DB랑 가까운 Entity가 나오지 않게 분리 , Service에서 리팩토링해서 controller로 내보내라
    
    private final MemoService service; // 서비스를 연결 = 주입한다고 표현 DI (dependency Injection)

    @GetMapping("/api/memos")   // Get방식
    public List<ResponseDto> getMemos(){    // 여러 Dto를 List로 Client에 보내기
        return service.getMemo();
    }

    @GetMapping("/api/memos/{id}")  // PUT방식
    public ResponseDto getMemo(@PathVariable Long id){    // 하나의 Dto를 Client에 보내기
        return service.getMemo(id);
    }

    @PostMapping("/api/memos")  // POST방식
    public ResponseDto createMemo(@RequestBody RequestDto dto){  // 들어오는건 Request 나가는건 Response로 각각 달리 할 수 있게 함
        return service.createMemo(dto);
    }

    @PutMapping("/api/memos/{id}") // PUT방식
    public ResponseDto updateMemo(@PathVariable Long id, @RequestBody RequestDto dto){   // 경로에서 id값 꺼내기. Body에서 값 꺼내기
        return service.updateMemo(id, dto);
    }

    @DeleteMapping("/api/memos/{id}")   // Delete방식
    public ResultDto deleteMemo(@PathVariable Long id, @RequestBody RequestDto dto){
        return service.deleteMemo(id, dto);
    }
}

 

 

 

 


10. Service를 생성한다.

package com.example.memopractice.service;

import com.example.memopractice.dto.*;
import com.example.memopractice.entity.Memo;
import com.example.memopractice.repository.MemoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@Service    // Service임을 선언
@RequiredArgsConstructor    // 필수적인 정보를 세팅하는 생성자를 생성 어노테이션
public class MemoService {

    private final MemoRepository repository; // Repository 주입

   
    public List<ResponseDto> getMemo() {
        List<Memo> memos = repository.findAllByOrderByModifiedAtDesc();     // 10-1 참조 : Repository에 검색 관련 메소드를 정의해야함
                                                                            // 10-2 참조 : Memo Entity에 Dto값을 받아 객체를 생성할 수 있도록 추가
        List<ResponseDto> exportDtoList = new ArrayList<>();
        for(Memo memo : memos){                                             // Entity -> Dto로 변환
            ResponseDto tempDto = new ResponseDto(memo);
            exportDtoList.add(tempDto);
        }
        return exportDtoList;
    }


    public ResponseDto getMemo(Long id) {
        Memo memo = repository.findById(id).orElseThrow(()-> new IllegalArgumentException("글이 없어요!"));  // 검색결과가 없으면 발생하는 예외를 처리
        return new ResponseDto(memo);                                       // Entity -> Dto로 변환
    }


    public ResponseDto createMemo(RequestDto dto) {
        Memo memo = new Memo(dto);
        repository.save(memo);
        return new ResponseDto(memo);
    }



    @Transactional
    public ResponseDto updateMemo(Long id, RequestDto dto) {
        Memo memo = repository.findById(id).orElseThrow(()-> new NullPointerException("글이 없습니다!"));
        if(memo.getPassword().equals(dto.getPassword())){
            memo.updateMemo(dto);              // 10-3 Entity 사항을 업데이트하는 메소드 추가
//            repository.save(memo);		// @Transactional 어노테이션을 쓰면 이건 생략, 어노테이션을 사용안하려면 이걸 추가
            return new ResponseDto(memo);
        } else {
            throw new IllegalArgumentException("비밀번호가 다릅니다");
        }
    }


    public ResultDto deleteMemo(Long id, RequestDto dto) {
        Memo memo = repository.findById(id).orElseThrow(()-> new IllegalArgumentException("글을 찾을 수 없습니다."));
        if(memo.getPassword().equals(dto.getPassword())){
            repository.delete(memo);
            return new ResultDto(true);
        } else {
            return new ResultDto(false);
        }
    }
}

 


 

10-1. Repository에 검색 관련 메소드 정의 

public interface MemoRepository extends JpaRepository <Memo, Long>{
    // 결과 필터링을 정의하고 출력할 타입을 정의하는 계층
 
    //추가부분
    List<Memo> findAllByOrderByModifiedAtDesc();        // findAllBy(모두찾는다)/OrderBy(-를 기준으로 정렬로)/ModifiedAt(ModifiedAt멤버변수를)/Desc(내림차순)
    
}

 


 

10-2. Memo Entity에 Dto값을 받아 객체를 생성할 수 있도록 "생성자" 추가

public Memo (RequestDto dto){   // 10-2 매개값 받는 생성자 추가
    this.title = dto.getTitle();
    this.author = dto.getAuthor();
    this.contents = dto.getContents();
    this.password = dto.getPassword();
}

 


 

10-3. Memo Entity에 Dto값을 받아 객체를 수정할 수 있도록 "메소드" 추가

public void updateMemo(RequestDto dto){
    this.title = dto.getTitle();
    this.author = dto.getAuthor();
    this.contents = dto.getContents();
//  this.password = dto.getPassword(); // password도 바꾼다면 포함
}

 

 

 

 


11. 시간 추적 기능을 사용하려면 실행 클래스에 @EnableJpaAuditing 어노테이션을 추가한다. 

package com.example.memopractice;

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

@EnableJpaAuditing  // <--- 이걸 추가
@SpringBootApplication
public class MemoPracticeApplication {

    public static void main(String[] args) {
        SpringApplication.run(MemoPracticeApplication.class, args);
    }
}

 

 

 

 


12. 구현을 완료했다면 테스트는 Postman으로 진행

아래 6가지 사항을 맞춰 각각 요청을 테스트

 

① method 방식 선택

② URL

③ Request 방식

④ json 문자열 형태로 그대로 값을 전달하고 싶으면 raw선택

⑤ raw 중에도 방식 중 JSON을 선택

⑥ 필요한 정보를 key갑과 value값에 맞춰 기입

 

그리고 send하면 아래에 Response가 출력된다.

 

 

 

 

 


13. DB 변경도 확인

+ Recent posts