Back-end/Spring

[SpringBoot] 2주차 스터디 (JPA, domain, repository, DTO, service, controller)

poppy 2021. 8. 28. 12:39
반응형
Spring JPA 란?

SQL을 쓰지 않고 데이터를 생성, 조회, 수정, 삭제 (CRUD) 할 수 있도록 해주는 번역기

 

프로젝트 구조는 다음과 같다.

 

 

1. Domain, Repository

  • Domain - DB의 테이블 역할을 하는 클래스
  • Repository - DB의 SQL 역할을 하는 인터페이스

 

먼저 Course.java 파일을 생성한다. 생성자를 직접 만들지 않고 Lombok을 사용하여 getter가 자동 생성 되도록 하였다.

@Getter // Lombok이 getter 자동 생성
@NoArgsConstructor // 기본 생성자를 대신 생성
@Entity // 그냥 클래스 아니고 DB의 테이블 역할을 하는 클래스라는 것을 알려줌
public class Course extends Timestamped{ // 생성일자, 수정일자 자동 갱신할 수 있도록 상속받음

    @Id // ID 값, Primary Key로 사용하겠다는 의미
    @GeneratedValue(strategy = GenerationType.AUTO) // id 생성시 자동으로 증가해줌
    private Long id;

    @Column(nullable = false) // 컬럼 값이 null이 아니고 반드시 값이 존재해야 함을 나타냄
    private String title;

    @Column(nullable = false)
    private String tutor;

    // 생성자
    public Course(CourseRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.tutor = requestDto.getTutor();
    }

    public Course(String title, String tutor) {
        this.title = title;
        this.tutor = tutor;
    }

    public void update(CourseRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.tutor = requestDto.getTutor();
    }
}

 

생성일자와 수정일자가 자동으로 갱신되도록 Timestamped 클래스를 만들어 상속받도록 하였다. 

@MappedSuperclass // 상속했을 때, 컬럼으로 인식하게 함
@EntityListeners(AuditingEntityListener.class) // 생성/수정 시간을 자동으로 반영하도록 설정
public abstract class Timestamped { // abstract - 직접 구현을 불가능하고 상속으로만 쓸 수 있음을 명시

    @CreatedDate // 생성 일자
    private LocalDateTime createdAt;

    @LastModifiedDate // 수정 일자
    private LocalDateTime modifiedAt;
}

 

CourseRepository.java 파일을 생성한다. 이 파일은 꼭! 인터페이스로 생성해야한다. JPA를 사용하기 위해 JpaRepository<domain 클래스, id의 type> 을 상속받는다.

public interface CourseRepository extends JpaRepository<Course, Long> { // domain 클래스, id의 type
}

 

2. DTO (Data Transfer Object)

- 테이블에 값을 넣을 때 Course 클래스를 직접 사용하는 것은 좋지 않다. 그래서 완충재로 활용하는 것이 DTO 이다.

 

@Setter // Lombok이 getter, setter 자동 생성
@Getter
@RequiredArgsConstructor // 꼭 필요한 요소(final) 자동 생성
public class CourseRequestDto {
    private final String title; // final 은 꼭 필요한 요소임을 명시하는 것, 값이 변경 될 수 없음
    private final String tutor;
}

 

3. Service, Controller

  • Controller - 가장 바깥 부분, 요청/응답을 처리한다
  • Service - 중간 부분, 실제 중요한 동작이 많이 일어나는 부분이다
  • Repo(Domain, Repository) - 가장 안쪽 부분, DB와 맞닿아 있다

 

CourseService.java 파일을 생성한다. Course 객체를 직접 변경하는 것은 좋지 않으므로 DTO를 사용해 값을 변경한다.

@RequiredArgsConstructor // 꼭 필요한 요소(final) 자동 생성
@Service // 이 클래스는 서비스임을 알려줌
public class CourseService {

    private final CourseRepository courseRepository; // final 은 꼭 필요한 요소임을 명시하는 것, 값이 변경 될 수 없음
    
    @Transactional // SQL 쿼리가 일어나야 함을 스프링에게 알려줌, 자동으로 DB에 업데이트 됨
    public Long update(Long id, CourseRequestDto requestDto) {
    
        // id로 Course 객체를 찾음. id에 해당하는 값이 없다면 메시지로 알려줌
        Course course1 = courseRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
        );
        course1.update(requestDto); // 값 업데이트
        return course1.getId();
    }
}

 

CourseController.java 파일을 생성한다. Controller는 url로 들어온 요청과 응답을 처리한다.

@RequiredArgsConstructor // 꼭 필요한 요소(final) 자동 생성
@RestController // JSON 으로 응답하기 위한 RestController 라는 의미
public class CourseController {

    private final CourseRepository courseRepository;
    private final CourseService courseService;

    // 등록된 전체 강의 목록 조회
    @GetMapping("/api/courses") 
    public List<Course> getCourses() {
        return courseRepository.findAll();
    }
    
    // 신규 강의 생성
    @PostMapping("/api/courses")
    public Course createCourse(@RequestBody CourseRequestDto requestDto) { // @RequestBody 는 응답받는 부분이라는 의미

        // 저장하는 것은 Dto가 아니라 Course이니, Dto의 정보를 course에 담음
        Course course = new Course(requestDto);
        return courseRepository.save(course); // 값 저장
    }

    // 강의 정보 변경
    @PutMapping("/api/courses/{id}") // {id}는 아이디 값이 오는데 유동적인 값이라는 의미
    public Long updateCourse(@PathVariable Long id, @RequestBody CourseRequestDto requestDto) { // @PathVariable - {}로 감싸준 값을 가리킴
        return courseService.update(id, requestDto); // 값 변경
    }

    // 강의 삭제
    @DeleteMapping("/api/courses/{id}")
    public Long deleteCourse(@PathVariable Long id) {
        courseRepository.deleteById(id); // 값 삭제
        return id;
    }
}

 

기본적인 CRUD를 할 수 있는 코드는 작성이 완료되었다. ARC를 사용해 CRUD가 성공적으로 이루어지는지 확인할 수 있다. 프로젝트를 Run 한 다음 ARC에 요청할 url을 입력하면 된다. POST 로 데이터를 저장한 후 GET 으로 데이터를 조회해야 다음과 같은 화면을 볼 수 있다.

 

POST / GET

 

PUT / DELETE

반응형