
이 시리즈의 내용은 작성자가 학습하며 배운 내용으로
주관적인 해석과 부정확한 개념이 있을 수 있습니당
시험기간 한 주를 건너뛰고 두 번째 발표이다. 전공수업보다 훨씬 더 타이트하게 나가는 것 같다. 야생에서 배우는 기분? 내가 잘 정리된 티스토리를 배끼는 수준에서 그치지 않기 위해서는 반드시 나의 실습이 들어가야 한다는 생각이 들었다. 파트장님이 천사같은 분이라 내가 잘못된 내용으로 해가도 '어 이건 이게 아니구요 이거예요..!' 이러실 것 같아서... 우선 실습을 해보기로 했다. 오늘 작성한 글은 전혀 검증되지 않은 내용이다!
결국 내가 해야할 건 백엔드 프로그래밍 즉 데이터를 적절하게 다루는 것이다. 데이터는 DB에 저장하는데 이 DB를 컨트롤하기 위한 명령어(쿼리)를 쉽게 다루기 위해 만들어진 것이 JPA이다.
JPA = java to SQL Query 컴파일러
근데 능동적으로 table까지 설계해주는
Spring Boot의 다양한 layer중 하나인 Repository layer는 DB의 table과 직접 작용할 수 있는 구조를 하고 있다. Repository가 DB에 관련된 모든 작업을 하는데, JPARepository는 위에서 설명한 JPA를 이용하여 DB를 조작하는 Repository이다.
JPARepository = JPA를 이용하여 DB를 컨트롤하는 Repository
그럼 다른 종류의 레포지도리도 있을까?
JPA를 사용하지 않고도 레포지토리를 구현할 수 있다.
기본적인 CRUD기능만 제공하는 레포지토리인 CRUDRepository가 있는데, JPARepository는 이 CRUDRepository를 상속받아 확장 기능(페이징, 정렬)을 추가한 것이라고 한다.
그럼 JPARepository를 실제로 사용해보면서 어떤식으로 DB에 기록되는지 관찰해보자.
우선 spring initializr로 프로젝트를 만들어준다.

의존성에 spring data jpa를 추가했다. 나머지도 실습에 쓸 것 같아서 그냥 미리 했다.
그리고 GENERATE를 누르면 압축된 프로젝트 파일을 다운받을 수 있다. 이제 이걸 IntelliJ로 열 것이다. 나는 학교에서 얼티밋을 제공해줘서 얼티밋으로 사용하고 있다. 학교 홈페이지 지원 목록에는 없었는데 student로 다운받고 학교 이메일로 로그인하니 사용 가능하더라... (홍보좀 하지)
다운받은 파일을 적당한 디렉터리로 옮기고 압축을 해제했다. 그리고 파일-열기로 선택해서 열어준다.

바로 이렇게 Gradle 빌드 스크립트를 찾았다고 로드하는 버튼이 나온다. 저번 포스팅에서 Gradle이 뭔지 배웠다. 의존성과 이런저런 설정(jdk는 뭘 쓰고 어떤 라이브러리 쓰고...)을 저장해두는 파일이다. 이걸 로드만 해주면..! 뭔가 알아서 쫙쫙 진행이 된다.
GDG에서는 블로그를 만드는 것이 목표이다. 따라서 post를 저장해볼 것이다. h2 데이터베이스에 대한 설정을 하기 위해서 yml 파일을 만들었다.
그리고 Controller, Service, Dto, Entity, Repository 각각 폴더와 class를 만들어준다.

좋다. 이제 JPARepository를 이용해 저장할 엔티티(Post)를 만든다.
package com.example.springtry.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
}
id와 내용만 가지는 아주 간단한 엔티티이다.
이제 이 Post를 저장하는 레포지토리 인터페이스를 만들어보자.
package com.example.springtry.repository;
import com.example.springtry.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
여기서 주목할 내용!!
extends JpaRepository<Post, Long>
PostRepository는 JpaRepository를 상속받는다는 것이다. 이렇게 JPARepository가 제공하는 기능을 사용할 수 있는 레포지토리가 된다.
또 상속에서 제네릭 형식으로 저장할 class와 PK를 받는데, PK가 뭔지 정확하게는 모르겠지만 유일한 값을 구분하는 key값인 것 같다. 우리는 long id를 PK로 사용하기 때문에 Long을 넣어준다.
이제 Post를 다뤄줄 Service를 만든다.
package com.example.springtry.service;
import com.example.springtry.dto.PostDot;
import com.example.springtry.entity.Post;
import com.example.springtry.repository.PostRepository;
import org.springframework.stereotype.Service;
@Service
public class PostService {
private final PostRepository postRepository;
public PostService(PostRepository postRepository){this.postRepository = postRepository;}
public String createPost(PostDot postDot){
Post post = Post.of(postDot);
postRepository.save(post);
return "post 작성 완료";
}
}
여기서도 중요한 점!!
private final PostRepository postRepository; public PostService(PostRepository postRepository){this.postRepository = postRepository;}
바로 서비스를 생성할 때 레포지토리를 인자로 전달해준다. (이게 의존성 주입인가..?) 아무튼 그럼 전달하고 싶은 레포지토리를 전달하면 거기에 작성하게 될 것이다. 또 본격적으로 사용하는 부분은
postRepository.save(post);
바로 여기이다. post entity를 전달하면 레포지토리가 알아서 쿼리를 작성하고 DB에 넣어준다. 그런데 이 코드에는 아직 구현되지 않은 것들이 있다. 바로 Post class의 of와 PostDto class이다.
package com.example.springtry.entity;
import com.example.springtry.dto.PostDot;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String content;
@Builder
public Post(Long id, String content){
this.id = id;
this.content = content;
}
public Post() {
this.content = "null";
}
public static Post of(PostDot postDot) {
return Post.builder()
.content(postDot.getContent())
.build();
}
}
이게 Post이다. of를 이용해서 Dto를 이용해서 객체 생성이 가능하도록 만들었다.
package com.example.springtry.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PostDot {
private String content;
}
이게 PostDto이다. id는 자동으로 할당되는 데이터이기 때문에 Dto에 포함할 필요는 없다. 이 Dto가 Post를 구성하는 모든 정보를 가지고 있기 때문에 이것만으로 Post를 만들 수 있다.
마지막으로 Controller에서 API를 설정해서 새로운 Post를 만들 수 있도록 해보자.
package com.example.springtry.controller;
import com.example.springtry.dto.PostDot;
import com.example.springtry.service.PostService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@Controller
@RestController
public class PostController {
private final PostService postService;
public PostController(PostService postService){this.postService = postService;}
@PostMapping("/new")
public ResponseEntity<String> createPost(@RequestBody PostDot postDot){
return ResponseEntity.status(HttpStatus.CREATED)
.body(postService.createPost(postDot));
}
}
Controller는 post를 받아서 postDto를 만들고, 그걸 postService에 createPost에 전달한다. 그리고 결과를 리턴하면 끝..!
이제 SpringApplication을 실행하면

에러 없이 잘 실행되었다..!
직접 Post를 보내보고 결과를 확인해보고 싶은데... 곧 발표라 스터디 이후에 다음 포스팅에서 Postman을 사용하여 데이터를 확인해보는 내용을 작성하겠다
역시 코딩은 직접 해봐야 한다. 처음부터 내가 하고 싶은대로 이해하며 만들어가니 Repository 뿐만 아니라 프로젝트의 전체적인 구조, 각 layer의 역할과 어떤식으로 상호작용하는지를 알 수 있었다. 수정, 삭제, 다른 entity와의 관계 등 더 복잡한 작업이 있겠지만 처음으로 이런 가벼운 생성을 만들어보니 뭐든 할 수 있을 것 같다.