소스 : https://github.com/braverokmc79/restapi-spring-boot-study
1.라이브러리 추가
spring-boot-starter-data-jpa h2 lombok validation hateoas springdoc-openapi-starter-webmvc-ui
예) pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.1.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>restfull-web-service</artifactId> <version>0.0.1-SNAPSHOT</version> <name>restfull-web-service</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.14.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency> <!-- Swagger 적용 Swagger 3.0.0부턴 1개의 종속성만 추가해도 된다. --> <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.2.0</version> </dependency> <!--- actuator --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-rest-hal-explorer</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
2. application.yml 설정 예
server:
port: 8088
spring:
# H2 Setting Info (H2 Console에 접속하기 위한 설정정보 입력)
#h2:
# console:
# enabled: false # H2 Console을 사용할지 여부 (H2 Console은 H2 Database를 UI로 제공해주는 기능)
# path: /h2-console # H2 Console의 Path
# Database Setting Info (Database를 H2로 사용하기 위해 H2연결 정보 입력)
datasource:
driver-class-name: org.h2.Driver # Database를 H2로 사용하겠다.
url: jdbc:h2:tcp://localhost/~/test # H2 접속 정보
username: sa # H2 접속 시 입력할 username 정보 (원하는 것으로 입력)
password: # H2 접속 시 입력할 password 정보 (원하는 것으로 입력)
output:
ansi:
enabled: always
devtools:
livereload:
enabled: true
restart:
enabled: true
messages:
basename : messages
# JPA 설정
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create # DB 초기화 전략 (none, create, create-drop, update, validate)
properties:
hibernate:
dialect: org.hibernate.dialect.H2Dialect
format_sql: true # 쿼리 로그 포맷 (정렬)
show_sql: true # 쿼리 로그 출력
defer-datasource-initialization: true
sql:
init:
mode: always
logging:
level:
org.springframework: info
springdoc:
packages-to-scan: com.example.restfullwebservice
default-consumes-media-type: application/json;charset=UTF-8
default-produces-media-type: application/json;charset=UTF-8
swagger-ui:
path: /
disable-swagger-default-url: true
display-request-duration: true
operations-sorter: alpha
#
#management:
# endpoints:
# web:
# exposure:
# include: "*"
스프링부트 3.0 변경사항
https://katastrophe.tistory.com/160
3. 목록 출력
controller
package com.example.restfullwebservice.user;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
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.server.mvc.WebMvcLinkBuilder.*;
@RestController
@RequestMapping("/jpa")
public class UserJpaController {
@Autowired
private UserRepository userRepository;
// http://localhost:8088/jpa/users or http://localhost:8088/users
@GetMapping("/users")
public List<User> retrieveAllUsers(){
return userRepository.findAll();
}
/**
* [Spring] RestAPI와 Hateoas로 링크 삽입하기*
*
* https://katastrophe.tistory.com/160
* @param id
* @return
*
*
* http://localhost:8088/jpa/users/1
*
*/
@GetMapping("/users/{id}")
public EntityModel<User> retrieveUser(@PathVariable int id){
Optional<User> user =userRepository.findById(id);
if(user.isEmpty()){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
// Resource
EntityModel<User> userEntityModel=EntityModel.of(user.get());
WebMvcLinkBuilder linkTo= linkTo(methodOn(this.getClass()).retrieveAllUsers());
userEntityModel.add(linkTo.withRel("all-users"));
return userEntityModel;
}
}
* 출력 예* 출력 예
*
* {
* "name": "User1",
* "joinDate": "2023-11-13T15:00:00.000+00:00",
* "ssn": "701010-11111",
* "_links": {
* "all-users": {
* "href": "http://localhost:8088/jpa/users"
* }
* }
* }
4.삭제
/**
* 삭제
* @param id
*/
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id){
log.info("**** deleteUser");
userRepository.deleteById(id);
}
5.등록
/** 등록
* insert into tbl_user (join_date,name,password,ssn,id) values (?,?,?,?,?)
* @param user
* @return
*/
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody User user){
User savedUser=userRepository.save(user);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
ResponseEntity<User> build = ResponseEntity.created(location).build();
log.info("** 유저 등록 : {}" , build);
//return ResponseEntity.status(HttpStatus.CREATED).body(build.toString());
return build;
}
{
"name": "User5",
"joinDate": "2023-11-14T15:00:00.000+00:00",
"password" :"1111",
"ssn": "411010-11111"
}
6.게시를 작성자 정보 가져오기
//게시글 및 작성자 정보 가져오기
//
// http://localhost:8088/jpa/users/1/posts
/**
*
* http://localhost:8088/jpa/users/1/posts
* @param id
* @return
*/
@GetMapping("/users/{id}/posts")
public List<Post> retrieveAllPostsByUser(@PathVariable int id ){
Optional<User> user= userRepository.findById(id);
if(user.isEmpty()){
throw new UserNotFoundException(String.format("ID[%s} not found", id));
}
return
/** 출력 =>
* [
* {
* "id": 1,
* "description": "My first post"
* },
* {
* "id": 2,
* "description": "My second post"
* }
* ]
*
*
*/
7.게시물작성
/**
*게시물 저장
* http://localhost:8088/jpa/users/{id}/posts
* http://localhost:8088/jpa/users/1/posts
*/
@PostMapping("/users/{id}/posts")
public ResponseEntity<?> createPost( @PathVariable int id, @Valid @RequestBody Post post){
Optional<User> user= userRepository.findById(id);
if(user.isEmpty()){
throw new UserNotFoundException(String.format("ID[%s} not found", id));
}
post.setUser(user.get());
Post savedPost = postRepository.save(post);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedPost.getId())
.toUri();
return ResponseEntity.created(location).build();
} /**
* 샘플 저장
* {
*
* "description":"Hello"
*
* }
*/
Post
package com.example.restfullwebservice.user;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "POST_ID")
private Integer id;
private String description;
// User : Post -> 1 : (0 :N), Main : Sub -> Parent -> Child
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnore
private User user;
}
User
package com.example.restfullwebservice.user;
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.persistence.*;
import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size;
import lombok.*;
import org.springframework.context.annotation.Primary;
import java.util.Date;
import java.util.List;
@Entity
@Getter
@Setter
@JsonIgnoreProperties(value = {"password" })
//@JsonFilter("UserInfo")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
@EqualsAndHashCode(of="id")
@Schema(description = "사용자 상세 정보를 위한 도메인 객체")
@Table(name = "TBL_USER")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Size(min=2, message = "Name 은 2글자 이상 입력해 주세요.")
@Schema(description = "사용자 이름을 입력해 주세요.")
private String name;
//과거데이터만 올수 있는 제약 조건
@Past
@Schema(description = "사용자 등록일을 입력해 주세요.")
private Date joinDate;
// @JsonIgnore
@Schema(description = "사용자 패스워드를 입력해 주세요.")
private String password;
// @JsonIgnore
@Schema(description = "사용자 주민번호를 입력해 주세요.")
private String ssn;
@OneToMany(mappedBy = "user")
private List<Post> posts;
@Builder
public User(Integer id, String name, Date joinDate, String password, String ssn){
this.id=id;
this.name=name;
this.joinDate=joinDate;
this.password=password;
this.ssn=ssn;
}
}
전체 controller 내용
package com.example.restfullwebservice.user;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.List;
import java.util.Optional;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
@RestController
@RequestMapping("/jpa")
@Slf4j
public class UserJpaController {
@Autowired
private UserRepository userRepository;
@Autowired
private PostRepository postRepository;
// http://localhost:8088/jpa/users or http://localhost:8088/users
@GetMapping("/users")
public List<User> retrieveAllUsers(){
return userRepository.findAll();
}
/**
* [Spring] RestAPI와 Hateoas로 링크 삽입하기*
*
* https://katastrophe.tistory.com/160
* @param id
* @return
*
*
* http://localhost:8088/jpa/users/1
*
* 출력 예
*
* {
* "name": "User1",
* "joinDate": "2023-11-13T15:00:00.000+00:00",
* "ssn": "701010-11111",
* "_links": {
* "all-users": {
* "href": "http://localhost:8088/jpa/users"
* }
* }
* }
*/
@GetMapping("/users/{id}")
public EntityModel<User> retrieveUser(@PathVariable int id){
Optional<User> user =userRepository.findById(id);
if(user.isEmpty()){
throw new UserNotFoundException(String.format("ID[%s] not found", id));
}
// Resource
EntityModel<User> userEntityModel=EntityModel.of(user.get());
WebMvcLinkBuilder linkTo= linkTo(methodOn(this.getClass()).retrieveAllUsers());
userEntityModel.add(linkTo.withRel("all-users"));
return userEntityModel;
}
/**
* 삭제
* @param id
*/
@DeleteMapping("/users/{id}")
public void deleteUser(@PathVariable int id){
log.info("**** deleteUser");
userRepository.deleteById(id);
}
/** 등록
* insert into tbl_user (join_date,name,password,ssn,id) values (?,?,?,?,?)
* @param user
* @return
*/
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody User user){
User savedUser=userRepository.save(user);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
ResponseEntity<User> build = ResponseEntity.created(location).build();
log.info("** 유저 등록 : {}" , build);
//return ResponseEntity.status(HttpStatus.CREATED).body(build.toString());
return build;
}
//게시글 및 작성자 정보 가져오기
//
// http://localhost:8088/jpa/users/1/posts
/**
*
* http://localhost:8088/jpa/users/1/posts
* @param id
* @return
*/
@GetMapping("/users/{id}/posts")
public List<Post> retrieveAllPostsByUser(@PathVariable int id ){
Optional<User> user= userRepository.findById(id);
if(user.isEmpty()){
throw new UserNotFoundException(String.format("ID[%s} not found", id));
}
return user.get().getPosts();
}
/** 출력 =>
* [
* {
* "id": 1,
* "description": "My first post"
* },
* {
* "id": 2,
* "description": "My second post"
* }
* ]
*
*
*/
/**
*게시물 저장
* http://localhost:8088/jpa/users/{id}/posts
* http://localhost:8088/jpa/users/1/posts
*/
@PostMapping("/users/{id}/posts")
public ResponseEntity<?> createPost( @PathVariable int id, @Valid @RequestBody Post post){
Optional<User> user= userRepository.findById(id);
if(user.isEmpty()){
throw new UserNotFoundException(String.format("ID[%s} not found", id));
}
post.setUser(user.get());
Post savedPost = postRepository.save(post);
URI location = ServletUriComponentsBuilder.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedPost.getId())
.toUri();
return ResponseEntity.created(location).build();
}
/**
* 샘플 저장
* {
*
* "description":"Hello"
*
* }
*/
}

















댓글 ( 4)
댓글 남기기