소스 : 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)
댓글 남기기