일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- type assertion
- CS
- zustand
- Headless 컴포넌트
- Microtask Queue
- 좋은 PR
- docker
- 프로세스
- 클라이언트 상태 관리 라이브러리
- 암묵적 타입 변환
- Sparkplug
- JavaScript
- jotai
- TypeScript
- Redux Toolkit
- 명시적 타입 변환
- useLayoutEffect
- mocking
- msw
- Compound Component
- prettier-plugin-tailwindcss
- Render Queue
- Recoil
- 회고
- react
- AJIT
- helm-chart
- linux 배포판
- Custom Hook
- 타입 단언
- Today
- Total
구리
[Spring Boot] REST API - Java Persistence API 사용 본문
Rest API에 대하여 인프런에서 들은 강의를 토대로 배운 내용을 정리한 것입니다. 참고로 강의는 Dowon Lee 님의 Spring Boot를 이용한 RESTful Web Services 개발 입니다.
목차
[JPA 사용을 위한 Dependency 추가 및 설정]
[Spring Data JPA를 이용한 Entity 설정과 초기 데이터 생성]
[JPA Service 구현을 위한 Controller, Repository 생성]
[JPA를 이용한 사용자 목록 조회 - GET HTTP Method]
[JPA를 이용한 사용자 추가 및 삭제 - POST/DELETE HTTP Method]
[게시물 관리를 위한 Post Entity와 User Entity와의 관계 설정]
[게시물 조회를 위한 Post Entity와 User Entity와의 관계 설정]
[JPA를 이용한 새 게시물 추가 - POST HTTP Method]
[JPA 사용을 위한 Dependency 추가 및 설정]
(1) pom.xml에 dependency 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
(2) application.yml에 설정 추가
spring:
jpa:
show-sql: true
h2:
console:
enabled: true
(3) SecurityConfig 재설정
package com.example.restful.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/h2-consloe").permitAll();
http.csrf().disable();
http.headers().frameOptions().disable();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("raccoon")
.password("{noop}test12") // noop : 어떤 인코딩도 없이 사용할 수 있는 no operation
.roles("USER");
}
}
(4) h2 database 접속
- localhost:8088/h2-console 에 접속
- mem : memory DB (현재 어플리케이션이 기동되는 동안에만 유지되는 DB)
- 어플리케이션이 종료되도 유지되는 DB를 원한다면 TCP 방식이 지원되는 드라이버 방식으로 변경하기
[Spring Data JPA를 이용한 Entity 설정과 초기 데이터 생성]
- User 도메인 재정의 (엔티티 설정 및 기본키 자동 생성)
package com.example.restful.user;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "All details about the user")
@Entity
public class User {
@Id
@GeneratedValue
private Integer id;
@Size(min=2, message = "Name은 2글자 이상 입력해주세요.")
@ApiModelProperty(notes = "사용자 이름을 입력해주세요")
private String name;
@Past
@ApiModelProperty(notes = "사용자의 등록일을 입력해주세요")
private Date joinDate;
@ApiModelProperty(notes = "사용자의 패스워드를 입력해주세요")
private String password;
@ApiModelProperty(notes = "사용자의 주민번호를 입력해주세요")
private String ssn;
}
- resource > data.sql 파일 생성 후 insert 쿼리문 작성, 그 후 서버 재구동
insert into user values(1, sysdate(), 'User1', 'tset1', '701010-2222222');
insert into user values(2, sysdate(), 'User2', 'tset2', '800707-2222222');
insert into user values(3, sysdate(), 'User3', 'tset3', '901212-2222222');
[JPA Service 구현을 위한 Controller, Repository 생성]
- JpaRepository 를 이용하여 전체 사용자 목록 조회 메소드 생성
(1) UserRepository Interface 생성
package com.example.restful.user;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
(2) UserJpaController에서 UserRepository를 이용한 전체 사용자 조회 메소드 구현
package com.example.restful.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
@Autowired
private UserRepository repository;
@GetMapping("/users")
public List<User> retrieveAllUser(){
return repository.findAll();
}
}
[JPA를 이용한 사용자 개별 조회 - GET HTTP Method]
- primary key 값을 이용하여 개별 사용자 조회
package com.example.restful.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Optional;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
... 중략
@GetMapping("/users/{id}")
public Resource<User> retrieveUser(@PathVariable int id){
Optional<User> user = repository.findById(id);
if(!user.isPresent()){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
// HATEOAS 사용
Resource<User> resource = new Resource<>(user.get());
ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUser());
resource.add(linkTo.withRel("all-users"));
// return user.get();
return resource;
}
}
[JPA를 이용한 사용자 추가 및 삭제 - POST/DELETE HTTP Method]
- 회원 정보를 삭제하는 deleteUser 메소드 구현
package com.example.restful.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.validation.Valid;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
... 중략
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id){
repository.deleteById(id);
}
}
- User 데이터 생성하는 HTTP POST 메소드 생성
package com.example.restful.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.validation.Valid;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
... 중략
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user){
User savesUser = repository.save(user);
// 사용자에게 요청 값을 변환해주기
// fromCurrentRequest() :현재 요청되어진 request값을 사용한다는 뜻
// path : 반환 시켜줄 값
// savedUser.getId() : {id} 가변변수에 새롭게 만들어진 id값 저장
// toUri() : URI형태로 변환
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savesUser.getId())
.toUri();
return ResponseEntity.created(location).build();
}
}
- User domain class에서 id(primary key) 값을 자동으로 생성해주는 sequence로 설정해놓음
- 하지만 이전에 insert sql문에서 1번 데이터가 저장된 상태기에 sequence를 통해 id="1" 로 설정한 값을 추가하면 중복으로 인해 오류 발생
- 초기 데이터 값을 JPA와 상관없이 등록시켰기에 => 임의의 데이터 (큰 값)으로 수정해놓기
insert into user values(100001, sysdate(), 'User1', 'tset1', '701010-2222222');
insert into user values(100002, sysdate(), 'User2', 'tset2', '800707-2222222');
insert into user values(100003, sysdate(), 'User3', 'tset3', '901212-2222222');
- Header 값을 조회하면 지금 추가한 데이터 조회(사용)하기 위해 어떤 key 값을 사용해야 하는지 알려줌 (http://localhost:8088/jpa/user/1) -> ResponseEntity 를 사용했기 때문
[게시물 관리를 위한 Post Entity와 User Entity와의 관계 설정]
- Post : User = N : 1 의 관계를 Post Entity 추가하여 관계 설정 (양방향)
(1) Post Entity class 생성
- @ManyToOne 어노테이션에서 fetch = FetchType.LAZY 속성값 의미 : 지연로딩 방식으로 연관 관계에 있는 Entity를 가져오지 않고, getter로 접근시 가져옴 <-> Eager
package com.example.restful.user;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Post {
@Id
@GeneratedValue
private Integer id;
private String description;
@JsonIgnore // 데이터 주고 받을 때 해당 데이터는 Ingonre되어서 응답값에 보이지 않음
@ManyToOne(fetch = FetchType.LAZY) // 지연로딩 방식 : 연관 관계에 있는 Entity를 가져오지 않고, getter로 접근시 가져옴 <-> Eager
private User user;
}
(2) User domain class에 Post 칼럼 추가 및 연관관계 설정 및 새로운 생성자 추가
- Post 객체가 연관관계의 주인이므로 User 도메인에서는 @OneToMany에 mappedBy 추가
package com.example.restful.user;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
// 원하는 이름으로 설정 (컴트롤러나 서비스 클래스에서 사용)
//@JsonFilter("UserInfo")
@ApiModel(description = "All details about the user")
@Entity
public class User {
@OneToMany(mappedBy = "user")
private List<Post> posts;
@Id
@GeneratedValue
private Integer id;
@Size(min=2, message = "Name은 2글자 이상 입력해주세요.")
@ApiModelProperty(notes = "사용자 이름을 입력해주세요")
private String name;
@Past
@ApiModelProperty(notes = "사용자의 등록일을 입력해주세요")
private Date joinDate;
@ApiModelProperty(notes = "사용자의 패스워드를 입력해주세요")
private String password;
@ApiModelProperty(notes = "사용자의 주민번호를 입력해주세요")
private String ssn;
public User(int id, String name, Date joinDate, String password, String ssn) {
this.id = id;
this.name = name;
this.joinDate = joinDate;
this.password = password;
this.ssn = ssn;
}
}
(3) data.sql에서 post 초기 데이터값 설정
insert into user values(100001, sysdate(), 'User1', 'tset1', '701010-2222222');
insert into user values(100002, sysdate(), 'User2', 'tset2', '800707-2222222');
insert into user values(100003, sysdate(), 'User3', 'tset3', '901212-2222222');
insert into post values(10001, 'My first post', 100001);
insert into post values(10002, 'My second post', 100002);
(4) 데이터 추가 확인
[게시물 조회를 위한 Post Entity와 User Entity와의 관계 설정]
- UserJpaController에 특정 사용자 게시물 조회 메소드 추가
// UserJpaController
@GetMapping("/users/{id}/posts")
public List<Post> retrieveAllPostsByUser(@PathVariable int id){
Optional<User> user = repository.findById(id);
if(!user.isPresent()){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
return user.get().getPosts();
}
[JPA를 이용한 새 게시물 추가 - POST HTTP Method]
- PostRepository 생성
package com.example.restful.user;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PostRepository extends JpaRepository<Post, Integer> {
}
- UserJpaController 메소드 추가 (사용자 정보를 검색한 후 그 정보의 id 값을 post에 지정)
@PostMapping("/users/{id}/posts")
public ResponseEntity<User> createPost(@RequestBody Post post, @PathVariable int id){
Optional<User> user = repository.findById(id);
if(!user.isPresent()){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
post.setUser(user.get());
Post savedPost = postRepository.save(post);
// 사용자에게 요청 값을 변환해주기
// fromCurrentRequest() :현재 요청되어진 request값을 사용한다는 뜻
// path : 반환 시켜줄 값
// savedUser.getId() : {id} 가변변수에 새롭게 만들어진 id값 저장
// toUri() : URI형태로 변환
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedPost.getId())
.toUri();
return ResponseEntity.created(location).build();
}