이 글은 jojoldu님의 과정을 보고 학습하며 작성한 글입니다. 좋은 글을 써주셔서 감사합니다!
- Using Language : Java8
- Using Tools : IntelliJ, SourceTree, SpringBoot1.5.10, Gradle
1. 도메인 코드 생성
src/main/java/com/udud/webervice/domain 패키지에 도메인 코드를 생성하기 위해
Posts.class파일과 PostsRepository인터페이스를 생성해준다.
이 Posts 클래스는 JPA와 밀접한 연관이 있는 클래스로, Posts.class의 코드는 다음과 같이 적어준다.
<Posts.java>
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class Posts {
@Id
@GeneratedValue
private Long id;
@Column(length = 500, nullable = false)
private String title;
@Column(columnDefinition = "TEXT", nullable = false)
private String content;
private String author;
@Builder
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
}
위 클래스를 살펴보면 여러 어노테이션들이 사용된 것을 확인할 수 있다.
먼저 JPA에서 제공해주는 어노테이션의 종류에 대해서 알아보자.
실제 DB테이블과 매칭될 클래스임을 알려줌
@Id
해당 테이블의 기본키(PK)필드를 직접 할당해주기 위해서 사용@GeneratedValue
기본키에 원하는 생성 규칙을 지정해준다.
기본키에 자동 생성 전략을 사용하기 위해서는 이 어노테이션을 선택해주며
원하는 키 생성 방법을 지정해주면 된다. 키 생성 방법(= IDENTITY, SEQUENCE, TABLE, AUTO)
기본값은 AUTO로 흔히 알고있는 AUTO_INCREMENT와 같은 뜻이다.@Column
해당 클래스의 필드는 모두 컬럼으로 생성되지만, 컬럼에 추가 변경 사항이 있을 경우에 사용
위의 코드에서는 컬럼 타입을 TEXT로 지정해주거나 길이를 500으로 설정, NULL값 허용 X로 지정
다음으로는 초기 프로젝트 설정시 지정해준 Lombok에서 제공해주는 어노테이션들이다.
- @NoArgsConstructor
기본 생성자를 자동으로 추가해주는 어노테이션으로 뒤에 붙은
(access = AccessLevel.PROTECTED)는 접근권한을 protected로 제한해 놓은 것이다.
프로젝트 코드에서 기본 생성자로 생성하는 것은 막으면서
JPA에서 Entity 클래스를 생성하는것은 허용하기 위해 추가한다고 한다. - @Getter
Getter 메소드는 필드의 값을 반환하는 메소드이며 Getter 메소드를 자동 생성해준다. - @Builder
해당 클래스의 빌더패턴 클래스를 생성해주고, 생성자 상단에 선언하면 생성자에 포함된 필드만 빌더에 포함한다.
많은 매개변수를 가지는 클래스를 설계할때 편리하다. (자바빈 패턴 보완)
2. 테스트 코드 작성
전 단계에서 생성한 Posts.class가 정상적으로 작동하는지 확인하기 위해서src/test/java/com/udud/webervice/domain/posts 패키지에 PostsRepositoryTest.java를 작성한다.
<PostsRepositoryTest.java>
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {
@Autowired
PostsRepository postsRepository;
@After
public void cleanup() {
postsRepository.deleteAll();
}
@Test
public void 게시글저장_불러오기() {
//given
postsRepository.save(Posts.builder()
.title("테스트 게시글")
.content("테스트 본문")
.author("susan7744850@gmail.com")
.build());
//when
List<Posts> postsList = postsRepository.findAll();
//then
// import를 다른 양식으로 했어서 애를 많이 먹었다.. asserThat 이자식.
// asserThat 양식을 잘 확인해주자.
Posts posts = postsList.get(0);
assertThat(posts.getTitle(), is("테스트 게시글"));
assertThat(posts.getContent(), is("테스트 본문"));
}
}
위의 코드가 잘 실행되는지 확인하기 위해서 게시글저장_불러오기()코드를 Run해줍니다.
성공적으로 코드가 실행되지만 실제로 값이 저장되어있는지 확인할 수가 없습니다.
(로컬DB사용이 아닌 H2메모리 DB를 사용했기 때문)
다음 단계는 이런 의문을 해결하기 위해서 진행됩니다.
3. Controller & DTO 구현
지난 시간에 작성했던 WebRestController에 코드를 추가해줍니다.
경로 : src/main/java/com/udud/webservice/web/WebRestController
<WebRestController.java>
@RestController
@AllArgsConstructor
public class WebRestController {
private PostsRepository postsRepository;
@GetMapping("/hello")
public String hello() {
return "HelloWorld";
}
// 추가 부분
@PostMapping("/posts")
public void savePosts(@RequestBody PostsSaveRequestDto dto){
postsRepository.save(dto.toEntity());
}
}
위 코드에서 작성된 @RestController는 Rest API 나 Web API를 개발하기 위한 어노테이션이며,
데이터를 문자열이나 JSON형식으로 출력해주는 컨트롤러이다.
@Controller과 @ResponseBody를 같이 사용한 것과 동일하다
@GetMapping과 @PostMapping은 @RequestMapping의 변형으로,
HTTP 메서드가 이미 GET, POST로 설정된 것
@AllArgsConstructor 어노테이션은 클래스에 선언하면 모든 멤버변수를 생성자에 참여 시킨다.
다음으로는 WebRestController에서 사용할 DTO 클래스를 생성한다.
경로 : src/main/java/com/udud/webservice/dto/posts/PostsSaveRequestDto
@Getter
// Controller에서 @RequestBody로 외부에서 데이터를 받는 경우에는 setter사용
@Setter
@NoArgsConstructor
public class PostsSaveRequestDto {
private String title;
private String content;
private String author;
public Posts toEntity(){
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}
4. Postman & 웹 컨솔로 DB 확인
먼저 Postman이란 구글 크롬의 확장 프로그램이다.
크롬 - 메뉴 - 도구 더보기 - 확장 프로그램 - Chrome 웹 스토어 열기를 선택하여 postman을 CHROME에 추가해준다.
그 후 postman을 실행시켜준다.
다음으로 H2 DB를 사용하기 위해서 H2를 활성화 시키는 코드를 추가해준다.
이 코드는 application.properties에 추가 해주면된다.
또한 application.properties의 이름을 application.yml로 변경해준다. (상대적으로 유연한 구조)
경로 : src/main/resources/application.yml
spring:
h2:
console:
enabled: true
위 과정까지 마친 후 Application.java를 실행해 준다.
스프링 부트가 실행되며, 브라우저에 localhost:8080/h2-console 를 입력하게 되면
다음과 같은 창이 뜨게 된다.
창이 뜬 기본값 그대로 Connect하게 되면 다음과 같은 창으로 넘어간다.
이 창에는 문제가 있는것을 알 수 있다.
자바로 생성한 POSTS테이블이 왼쪽 리스트에 나타나지 않는ㅠㅠ
jojoldu 글의 댓글을 살펴보니 문제를 해결하는 방법을 알게 되었다.
바로 JDBC URL 재설정 다음 사진과 같이 URL을 변경해준다.
URL 재설정 후 Connect시키면 다음과 같이 테이블이 생성된 모습을 확인할 수 있다.
생성된 테이블에 입력된 데이터를 확인하기 위해서 SELECT 쿼리문을 입력해서 데이터를 조회한다.
쿼리문을 날려본 결과 아무것도 데이터가 입력되지 않은 모습을 볼 수가 있으며,
생성된 POSTS테이블에 컬럼으로 생성한 ID, AUTHOR, CONTENT, TITLE가 생성된 것을 볼 수 있다.
테이블과 컬럼 생성이 잘 된것을 확인하였으므로 설치한 Postman을 이용해서 데이터를 입력해준다.
Body안에 다음과 같은 데이터를 입력해주고 파란색 Send버튼을 클릭하여 데이터를 전송해준다.
그 후 다시 localhost:8080/h2-console을 확인해보면
Postman에 입력한 값이 그대로 DB에 입력된 모습을 볼 수 있다.
5. 입력시간 , 수정시간 자동 입력
POSTS 테이블에 ID, AUTHOR, CONTENT, TITLE이 잘 들어간 모습까지 확인을 했다.
일반적으로 데이터를 입력한 시간과 수정한 시간은 데이터의 변화에 중요한 정보이므로
자동으로 시스템 시간이 입력되도록 하는 과정을 학습해보자.
시스템 시간을 등록, 수정하는 코드가 여러번 들어가면 코드가 복잡해지므로
JPA Auditing을 이용해서 간단하게 과정을 해결할 것이다.
코드를 작성하기전, 로컬의 시간을 가져오는 LocalDate와 LocalDateTime이 DB에 저장시킬시
정상 전환이 이루어지지 않아 다음과 같은 2줄의 코드를 추가하여 이러한 문제를 해결한다.
<build.gradle>
classpath "io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE"
ext['hibernate.version'] = '5.2.11.Final'
코드를 추가하면
Hibernate의 버전을 바꾸겠다는 의미라고한다
시스템 시간을 사용할 수 있는 설정이 끝나면 생성, 수정 시간을 처리할 코드를 작성해준다.
생성, 수정 시간을 처리해줄 BaseTimeEntity.java를 생성해준다.
경로 : src/main/java/com/udud/webservice/domain/BaseTimeEntity
<BaseTimeEntity.java>
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
@CreatedDate
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime modifiedDate;
}
- @MappedSuperclass
부모 클래스를 상속받는 자식 클래스에게 매핑 정보만 제공하고 싶을 때 사용
하지만 엔티티는 엔티티만 상속받을 수 있기 때문에 클래스를 상속받기 위해 사용
엔티티 종류에 상관없이 공통으로 가지고 있어야 하는 정보가 있을때 많이 사용
(ex: 데이터 생성, 수정시간 등) - @CreatedDate
처음 엔티티가 저장될때 날짜를 주입 - @LastModifiedDate
엔티티가 수정될때 수정날짜 주입
이렇게 만든 부모 클래스 BasetimeEntity를 Posts클래스가 상속 받게 해준다.
상속 방법은 간단히 extends만 추가해주면 된다.
public class Posts extends BaseTimeEntity {
......
}
삽입, 수정 시간을 넣는 마지막 단계로는 이용하고 있는 JPA Auditing을 사용하기 위해 활성화 시켜주는 단계이다.
Application.java에 JPA Auditing을 사용하겠다는 어노테이션만 하나 추가해준다.
<Application.java>
@EnableJpaAuditing
// 추가
6. JPA Auditing 테스트 코드 작성
PostsRepositoryTest.java에 삽입, 수정 시간을 테스트하는 메소드를 하나 추가해준다.
본래의 코드 아래부분에 아래 코드를 삽입해준다.
<PostsRepositoryTest.java>
@Test
public void BaseTimeEntity_등록(){
// given
LocalDateTime now = LocalDateTime.now();
postsRepository.save(Posts.builder()
.title("테스트 게시글")
.content("테스트 본문")
.author("susan7744850@gmail.com")
.build());
// when
List<Posts> postsList = postsRepository.findAll();
// then
Posts posts = postsList.get(0);
assertTrue(posts.getCreatedDate().isAfter(now));
assertTrue(posts.getModifiedDate().isAfter(now));
}
마지막 단계까지 작성 후 테이블을 확인해본다.
컬럼이 생성된 모습을 확인할 수 있다.
이제 PostMan으로 데이터를 다시한번 전송해본다.
그 후 데이터를 확인해본 결과 시간이 잘 들어가 있는것을 확인할 수 있다.
7. 이번시간에 배운점
JPA와 H2, Postman에 대해서 처음 접하는 경험이라 생소하지만,
새로운 기술을 접하고 그에 대한 설명이 자세히 되어있는 글을 찾아 따라하다보니
금방 손에 익는 것 같다. 아무것도 모르는 상태에서 문법부터 하나하나 배우려니
시간이 오래걸리지만 무작정 코딩해서 git에 올리는 것보다 블로깅을 통하여 정리하니
더욱 기억에 남는 것같다. 어노테이션의 종류에 대해서 잘 알 수 있는 시간이였고,
데이터 수정, 삽입 시간을 사용하려면 앞으로 BaseTimeEntity만 상속받으면 된다니
정말 편리할 것 같다.