[RESTful Web Service]-6

EarlyBird·2021년 11월 1일
0

RESTful Web Service

목록 보기
6/6
post-thumbnail

Dowon Lee님의 Spring Boot를 이용한 RESTful Web Services 개발 강의를 학습한 내용입니다.

Java Persistence API

JPA 사용 설정

의존성 추가

        <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>

application.yml

spring:
  datasource:
    url: jdbc:h2:mem:testdb
  jpa:
    show-sql: true
    defer-datasource-initialization: true
  h2:
    console:
      enabled: true

security 수정

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/h2-console/**").permitAll();
        http.csrf().disable();
        http.headers().frameOptions().disable();
    }
    //...
}
  • localhost:8088/h2-console

Spring Data JPA

data.sql

insert into user values(1, sysdate(), 'User1', 'test1111', '701010-1111111');
insert into user values(2, sysdate(), 'User2', 'test2222', '801010-1111111');
insert into user values(3, sysdate(), 'User3', 'test3333', '901010-1111111');
@Entity
public class User {

    @Id
    @GeneratedValue
    private Integer id;
    
    //...
}
  • @Entity : 해당 클래스 이름으로 DB에 테이블 생성, 클래스 필드 정보로 테이블 생성에 필요한 컬럼 정보로 사용
  • @Id : 기본 키
  • @GeneratedValue : 자동 생성되는 키 값

JPA Service 구현(Controller, Repository)

UserRepository

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
  • JpaRepository<다룰 Entity, 기본 키 타입>

UserJpaController

@RestController
@RequestMapping("/jpa")
public class UserJpaController {

    private UserRepository userRepository;

    public UserJpaController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/users")
    public List<User> retrieveAllUsers(){
        return userRepository.findAll();
    }
}

JPA를 이용한 사용자 목록 조회

    @GetMapping("/users/{id}")
    public User retrieveUser(@PathVariable int id){
        Optional<User> user = userRepository.findById(id);

        if(!user.isPresent()){
            throw new UserNotFoundException(String.format("ID{%s} not found", id));
        }

        return user.get();
    }

HATEOAS

    @GetMapping("/users/{id}")
    public EntityModel<User> retrieveUser(@PathVariable int id){
        Optional<User> user = userRepository.findById(id);

        if(!user.isPresent()){
            throw new UserNotFoundException(String.format("ID{%s} not found", id));
        }

        //HATEOAS
        EntityModel<User> model = new EntityModel<>(user.get());
        WebMvcLinkBuilder linkTo = WebMvcLinkBuilder.linkTo(
                WebMvcLinkBuilder.methodOn(this.getClass()).retrieveAllUsers());
        model.add(linkTo.withRel("all-users"));

        return model;
    }
}

JPA를 이용한 사용자 삭제, 추가

삭제

    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable int id){
        userRepository.deleteById(id);
    }

추가

data.sql 수정

insert into user values(90001, sysdate(), 'User1', 'test1111', '701010-1111111');
insert into user values(90002, sysdate(), 'User2', 'test2222', '801010-1111111');
insert into user values(90003, sysdate(), 'User3', 'test3333', '901010-1111111');
// 중복을 피하기 위한 임의값 설정
    @PostMapping("/users")
    public ResponseEntity<User> createUser(@Valid @RequestBody User user){
        User savedUser = userRepository.save(user);

        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(savedUser.getId())
                .toUri();

        return ResponseEntity.created(location).build();
    }

게시물 관리 - POST Entity, 초기 데이터 생성

data.sql 내용 추가

insert into post values(10001, 'My first post', 90001);
insert into post values(10002, 'My second post', 90001);
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Post {
    @Id
    @GeneratedValue
    private Integer id;

    private String description;

    @ManyToOne(fetch = FetchType.LAZY) 
    @JsonIgnore
    private User user;
}
  • User : Post -> 1 : (0~N)
  • Main(User) : Sub(Post) -> Parent : Child
  • @ManyToOne : 다 대 일
  • FetchType.LAZY : 지연 로딩 방식
  • @JsonIgnore : 외부에 데이터 노출X
public class User {
//...
   @OneToMany(mappedBy = "user")
   private List<Post> posts;

   public User(int id, String name, Date joinDate, String password, String ssn) {
       this.id = id;
       this.name = name;
       this.password = password;
       this.ssn = ssn;
   }
}
  • @OneToMany : 일 대 다

게시물 조회

    @GetMapping("/users/{id}/posts")
    public List<Post> retrieveAllPostsByUser(@PathVariable int id){
        Optional<User> user = userRepository.findById(id);

        if(!user.isPresent()){
            throw new UserNotFoundException(String.format("ID{%s} not found", id));
        }

        return user.get().getPosts();
    }

새 게시물 추가

PostRepository

@Repository
public interface PostRepository extends JpaRepository<Post, Integer> {
}

UserJpaController

public class UserJpaController {
    @Autowired
    private PostRepository postRepository;
    //...
       
    @PostMapping("/users/{id}/posts")
    public ResponseEntity<Post> createPost(@PathVariable int id, @RequestBody Post post){
        Optional<User> user = userRepository.findById(id);

        if(!user.isPresent()){
            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();
    }

}

REST API 설계 시 고려해야 할 사항

  • 해당 API 소비자 입장에서 간단하고 직관적이어야 한다.
  • HTTP 장점을 살려야 한다.
  • Request methods(GET, POST, PUT, DELETE)를 적절히 사용해야 한다.
  • 각 API 요청에 따른 적절한 Response Status(200, 404, ...)를 반환해야 한다.
  • 제공하는 URI는 민감정보를 포함하면 안된다.
  • 복수 형태의 URI를 사용하는 것이 좋다.
  • Resource에 대한 형태는 명사형이 좋다.
  • 일괄된 접근, End Point를 사용하는 것이 좋다.
profile
안되면 되게 합시다

0개의 댓글