Spring boot JPA 등록/수정/조회 API 제작

80829 단어 JavaSpring bootJPAJPA

등록/수정/조회 API 만들기

사용자 (User) 관련 로직

  • 사용자 회원가입 (Insert)
  • 사용자 탈퇴 (Delete)
  • 사용자 조회 (Selete)
  • 사용자 정보 변경 (Update)
  • 사용자는 ID는 중복되지 않는다.
  • 사용자 이름, 전화번호, 주민등록번호, 비밀번호를 가진다.
  • 이름, 전화번호, 주민등록번호, 비밀 번호는 필수 입력 사항이다.

1. Spring Web Layer

1-1. Web Layer

  • @Controller와 JSP 등의 뷰 템플릿 영역
  • 이외에도 필터(@Filter), 인터셉터, 컨트롤러 어드바이스(@ControllerAdvice)등 외부 요청과 응답에 대한 전반적인 영역을 얘기한다.

1-2. Service Layer

  • @Service에 사용되는 서비스 영역
  • 일반적으로 Controller와 DAO의 중간 영역에서 사용된다.
  • @Transactional이 사용되어야 하는 영역이기도 하다.

1-3. Repository Layer

  • DB와 같이 데이터 저장소에 접근하는 영역
  • DAO(Data Access Object) 영역

1-4. Dtos

  • DTO(Data Transfer Object) : 계층간에 데이터 교환을 위한 객체

1-5. Domain Model

  • 도메인이라 불리는 개발 대상을 모든 사람들이 동일한 관점에서 이해하고, 공유 할 수 있도록 단순화 시킨것.
  • 택시 앱을 예로 들면 배차, 탑승, 요금 등이 모두 도메인이 될 수 있다.
  • @Entity가 사용된 영역이 도메인 모델이다.

2. 트랜잭션 스크립트 패턴과 도메인 모델 패턴

2-1. 트랜잭션이란?

출처

  • 데이터베이스의 상태를 변경시키기 위해 수행하는 작업 단위.
  • SELECT, UPDATE, INSERT, DELETE와 같은 작업들이 포함되어있다.
  • 트랜잭션은 상황에 따라 여러개가 만들어질 수 있다.
  • 하나의 트랜잭션은 commit(저장)되거나 rollback(철회) 될 수 있다.
    • A, B, C, D 가 문제를 풀어 각각 100 Point 씩 얻었고 이를 DB에 저장하려 한다.
    • A 저장 완료, B 저장 완료, C 저장 완료, D 저장 실패
    • 이런 상황이 오게 된다면 잘못된 처리이므로 다시 저장을 수행해야 하는데, A, B, C는 그 전에 이미 저장이 완료되었기 때문에 다시 저장을 시작하면 200점이 저장된다.
    • 이런 문제점들을 위해서 트랜잭션은 Commit 과 Rollback 을 이용한다.
      • commit : 하나의 트랜잭션이 성공적으로 끝나서 데이터베이스가 일관성 있는 상태에 있음.
      • rollback : 트랜잭션의 원자성이 깨질 때(하나의 트랜잭션 처리가 비정상적으로 종료 되었을 때)의 상태. Rollback이 이루어 진다면 트랜잭션을 다시 실행하거나 부분적으로 변경된 결과를 취소할 수 있다.
  • 트랜젝션에는 4가지 특징이 존재한다.
    • 원자성 : 트랜잭션이 모두 DB에 반영되거나, 전혀 반영되지 않거나해야한다.
    • 일관성 : 작업 처리 결과가 항상 일관되어야 한다. 데이터 타입이 반환 전/후가 항상 동일해야한다.
    • 독립성 : 하나의 트랜잭션은 다른 트랜잭션이 끼어들 수 없고, 마찬가지로 독립적임. 각각의 트랜잭션은 서로 간섭이 불가능함
    • 지속성 : 트랜잭션이 성공적으로 완료되면 영구적으로 결과에 반영되어야함. 보통 commit이 되면 지속성은 만족 시킬 수 있다.

2-2. 트랜잭션 스크립트 정의

출처

  • 은행 계좌 이체 서비스의 경우 다음의 순서를 가진다.

    • 잔고 확인 -> 받는 사람 확인 -> 이체 실행 -> 잔고 감소
    • 이 순서가 하나의 로직으로 처리되어야 하고, 한번이라도 오류가 발생하면 모든 처리가 취소되고 모든 과정이 성공해야 처리가 완료된다. (All-or-Nothing)
    • 트랜젝션 스크립트 패턴은 이렇게 하나의 트랜잭션으로 구성된 로직을 단일 함수 또는 단일 스크립트에서 처리하는 구조를 가진다.

2-3. 도메인 모델 정의

출처

  • 객체 지향 분석 설계에 기반해 구현하고자 하는 도메인(비즈니스 영역)의 모델을 생성하는 패턴.
  • 도메인 모델은 비즈니스 영역에서 사용되는 객체를 판별하고, 객체가 제공해야 할 목록을 추출하며 각 객체관의 관계를 정립하는 과정을 거친다.
  • 명사와 동사를 구분해서 명사로 객체를 추출해내고, 동사로부터 객체의 기능 및 객체 사이의 관계를 유추해낸다.
  • 해당 글에서는 최대한 도메인 모델 패턴으로 코드를 작성할 것입니다.

3. User 등록

  • 패키지는 다음과 같이 구성되어있다.

3-1. UserDTO 구현

package com.test.blog.user.dto;


import com.test.blog.user.entity.User;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@NoArgsConstructor
public class UserDTO {
    private String id;
    private String phoneNumber;
    private String socialSecurityNumber;
    private String password;

    @Builder
    public UserDTO(String id, String phoneNumber, String socialSecurityNumber, String password){
        this.id = id;
        this.phoneNumber = phoneNumber;
        this.socialSecurityNumber =socialSecurityNumber;
        this.password = password;
    }

    public User toEntity(){
        return User.builder()
                .id(id)
                .phoneNumber(phoneNumber)
                .socialSecurityNumber(socialSecurityNumber)
                .password(password)
                .build();
    }
}
  • DTO를 구현한 이유
    • 절대로 Entity 클래스를 Request/Response에서 사용하면 안된다.
    • Entity 클래스는 DB와 맞닿은 핵심 클래스이기 때문.

3-2. UserService 구현

package com.test.blog.user.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import com.test.blog.user.entity.UserRepository;
import com.test.blog.user.dto.UserDTO;
import org.springframework.transaction.annotation.Transactional;

/**
 * 서비스는 실제 비즈니스 로직을 구현하는 단이다.
 */
@RequiredArgsConstructor
@Service
public class UserService {
    private final UserRepository userRepository;

    @Transactional
    public String save(UserDTO userDTO){
        return userRepository.save(userDTO.toEntity()).getId();
    }
}   

3-3. UserController 구현

package com.test.blog.user.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

import com.test.blog.user.dto.UserDTO;
import com.test.blog.user.service.UserService;

/**
 * Bean을 주입 받을 때 방법은 3가지가 있다.
 * @Autowired
 * setter
 * 생성자
 *
 * 이중 가장 권장되는 방법은 생성자로 주입받는것이다.
 * @RequiredArgsConstructor로 final이 선언된 모든 필드에 Bean을 주입받는다.
 */

@RequiredArgsConstructor
@Controller
public class UserController {


    private final UserService userService;

    @PostMapping("/api/user")
    public String save(@RequestBody UserDTO userDTO){
        return userService.save(userDTO);
    }
}
  • Bean을 주입받는 방법 3가지
    • @Autowired
    • setter
    • 생성자
  • Bean을 주입받을 때 가장 권장되는 방법은 생성자로 주입받는 방법이 있다. 생성자로 Bean 객체를 받도록 하면 @Autowired와 동일한 효과를 볼 수 있다.
  • @RequiredArgsConstructor로 final이 선언된 모든 필드를 생성자로 받게 한다.
  • Spring Bean이란?
    • IoC(제어의 역전, Inversion Of Control)특징 에 대해
      • 일반적인 자바 프로그래밍 방식은 각 객체들이 프로그램의 흐름을 결정하고 각 객체를 직접 생성 (new)및 조작하는 작업을 했다.
      • IoC가 적용될 경우에는 객체의 생성을 특별한 관리 위임 주체에게 맡긴다. 사용자는 객체를 직접 생성하지 않고 객체의 생명주기를 컨트롤하는 주체는 다른 주체가 된다.
      • 사용자의 제어권을 다른 주체에게 넘기는것을 IoC라고 부른다.
    • Spring에서는 new를 이요하여 생성한 객체가 아닌, Spring에 의해서 관리(IoC)당하는 자바 객체를 사용한다. 이렇게 스프링에서 생성되고 관리되는 자바 객체를 Bean이라고 부른다.

3-4. UserControllerTest 구현

package com.test.blog.user.controller;

import com.test.blog.user.entity.User;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;

import com.test.blog.user.entity.UserRepository;
import com.test.blog.user.dto.UserDTO;


import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {

    @LocalServerPort
    private int port;
    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private UserRepository userRepository;


    @After
    public void end() throws Exception{
        userRepository.deleteAll();
    }
    @Test
    public void save() throws Exception{
        String id = "JJStone";
        UserDTO userDTO = UserDTO.builder()
                .id(id)
                .password("1234")
                .phoneNumber("000-0000-0000")
                .socialSecurityNumber("000000-0000000")
                .build();
        String url = "http://localhost:" + port + "/api/user";
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, userDTO, String.class);
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isEqualTo(id);

        Optional<User> user_ = userRepository.findById(id);
        assertThat(user_).isNotEmpty();
        User user = user_.get();
        assertThat(user.getId()).isEqualTo(id);

    }
}
  • @WebMvcTest의 경우 JPA 기능이 작동하지 않기 때문에 사용하지 않음
  • @SpringBootTest와 TestRestTemplate 사용
  • 포트는 SpringBootTest.WebEnvironment.RANDOM_PORT 사용

4. User 수정/조회

4-1. User 수정

  • User

    
    package com.test.blog.user.entity;
    
    
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.RequiredArgsConstructor;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    
    @Getter
    @Entity
    @NoArgsConstructor
    public class User {
       
        ...
        
        public void update(String phoneNumber, String socialSecurityNumber, String password){
            this.phoneNumber = phoneNumber;
            this.socialSecurityNumber = socialSecurityNumber;
            this.password = password;
        }
    }
    
  • UserService

    package com.test.blog.user.service;
    
    import com.test.blog.user.entity.User;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    
    import com.test.blog.user.entity.UserRepository;
    import com.test.blog.user.dto.UserDTO;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Optional;
    
    /**
     * 서비스는 실제 비즈니스 로직을 구현하는 단이다.
     */
    @RequiredArgsConstructor
    @Service
    public class UserService {
        private final UserRepository userRepository;
    
        ...
    
        @Transactional
        public String update(String id, UserDTO userDTO){
            User user = userRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("해당 아이디가 없습니다."));
            user.update(userDTO.getPhoneNumber(), userDTO.getSocialSecurityNumber(), userDTO.getPassword());
            return id;
        }
    }
    
  • UserController

    package com.test.blog.user.controller;
    
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.*;
    
    import com.test.blog.user.dto.UserDTO;
    import com.test.blog.user.service.UserService;
    
    
    @RequiredArgsConstructor
    @RestController
    public class UserController {
    
    
        private final UserService userService;
    
        ...
        
        @PutMapping("/api/v1/user/{id}")
        public String update(@PathVariable String id, @RequestBody UserDTO userDTO){
            return userService.update(id, userDTO);
        }
    }
    
  • DB에 UPDATE 쿼리를 날리는 부분이 존재하지 않음.

  • 이유는 JPA 영속성 컨텍스트 때문.

  • 영속성 컨텍스트란 엔티티를 영구 저장하는 환경이다. 일종의 논리적 개념이며 JPA의 핵심 내용은 엔티티가 영속성 컨텍스트에 포함되어있는지 아닌지로 갈린다.

  • JPA 엔티티 매니저 (EntityManager)가 활성화된 상태로(Spring Data Jpa를 쓰면 기본 옵션이다.) 트랜잭션 안에서 DB 데이터를 가져올때 이 데이터는 영속성 컨텍스트가 유지된 상태가 된다.

  • 이 상태에서 데이터 값을 변경하면 트랜잭션이 끝나는 시점에 해당 테이블 변경분을 반영한다. 즉, Entity 객체의 값만 변경하면 별도의 UPDATE 쿼리를 날릴 필요가 없다. 더티체킹 이라고도 한다.

    • UserControllerTest (업데이트 테스트)
    package com.test.blog.user.controller;
    
    import com.test.blog.user.entity.User;
    import org.junit.After;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.boot.web.server.LocalServerPort;
    import org.springframework.boot.test.web.client.TestRestTemplate;
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpMethod;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.HttpStatus;
    
    import com.test.blog.user.entity.UserRepository;
    import com.test.blog.user.dto.UserDTO;
    
    
    import java.util.Optional;
    
    import static org.assertj.core.api.Assertions.assertThat;
    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class UserControllerTest {
    
        @LocalServerPort
        private int port;
        @Autowired
        private TestRestTemplate restTemplate;
    
        @Autowired
        private UserRepository userRepository;
    
    
        @After
        public void end() throws Exception{
            userRepository.deleteAll();
        }
        ... 
    
        @Test
        public void update() throws Exception{
            String id = "user1";
            User saveUser = userRepository.save(User.builder()
                    .id(id)
                    .password("1234")
                    .socialSecurityNumber("000000-0000000")
                    .phoneNumber("000-0000-0000")
                    .build());
    
            String updateId = saveUser.getId();
            String password = "5678";
            String socialSecurityNumber = "111111-1111111";
            String phoneNumber = "111-1111-1111";
    
            UserDTO userDTO = UserDTO.builder()
                    .password(password)
                    .socialSecurityNumber(socialSecurityNumber)
                    .phoneNumber(phoneNumber)
                    .build();
    
            String url = "http://localhost:" + port + "/api/v1/user/" + updateId;
    
            HttpEntity<UserDTO> requestEntity = new HttpEntity<>(userDTO);
    
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class);
    
            assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
            assertThat(responseEntity.getBody()).isEqualTo(id);
    
            Optional<User> user_ = userRepository.findById(updateId);
            assertThat(user_).isNotEmpty();
            User user = user_.get();
            assertThat(user.getPassword()).isEqualTo(password);
            assertThat(user.getPhoneNumber()).isEqualTo(phoneNumber);
            assertThat(user.getSocialSecurityNumber()).isEqualTo(socialSecurityNumber);
        }
    }

4-2. User 조회

  • UserDTO

    package com.test.blog.user.dto;
    
    
    import com.test.blog.user.entity.User;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    
    
    @Getter
    @NoArgsConstructor
    public class UserDTO {
        private String id;
        private String phoneNumber;
        private String socialSecurityNumber;
        private String password;
    
        ...
    
        public UserDTO(User user){
            this.id = user.getId();
            this.password = user.getPassword();
            this.socialSecurityNumber = user.getSocialSecurityNumber();
            this.phoneNumber = user.getPhoneNumber();
        }
    
    }
  • UserService

    package com.test.blog.user.service;
    
    import com.test.blog.user.entity.User;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Service;
    
    import com.test.blog.user.entity.UserRepository;
    import com.test.blog.user.dto.UserDTO;
    import org.springframework.transaction.annotation.Transactional;
    
    import    java.util.Optional;
    
    /**
     * 서비스는 실제 비즈니스 로직을 구현하는 단이다.
     */
    @RequiredArgsConstructor
    @Service
    public class UserService {
        private final UserRepository userRepository;
        
        ...
    
        public UserDTO findById(String id){
           User user = userRepository.findById(id).orElseThrow(() ->  new IllegalArgumentException("해당 아이디가 없습니다."));
           return new UserDTO(user);
        }
    }
    
  • UserController

    package com.test.blog.user.controller;
    
    import lombok.RequiredArgsConstructor;
    import org.springframework.web.bind.annotation.*;
    
    import com.test.blog.user.dto.UserDTO;
    import com.test.blog.user.service.UserService;
    
    @RequiredArgsConstructor
    @RestController
    public class UserController {
    
    
        private final UserService userService;
        
        ...
        @GetMapping("/api/v1/user/{id}")
        public UserDTO findById(@PathVariable String id){
            return userService.findById(id);
        }
    }
    

부록 - REST API

출처

  • REST란?

    • 정의

      • REpresentational State Transfer의 약자
      • 웹에 존재하는 모든 자원(이미지, 동영상, DB)에 고유한 URL을 부여하여 활용하는것을 의미
      • 자원을 정의하고 자원에 대한 주소를 지정하는 방법론.
    • 구성요소

      • 자원(Resource), URL : 모든 자원은 고유한 ID를 가지며 ID는 서버에 존재하고 클라이언트는 각 자원의 상태를 조작하기 위해 요청을 보낸다. HTTP에서 이러한 자원을 구별하는 ID는 strudent/1과 같은 HTTP URL이다.
      • 행위(Verb), Method : 클라이언트는 URL을 이용해 자원을 지정하고 자원을 조작하기 위해 Method를 사용한다. HTTP 프로토콜에서는 GET, POST, DELETE와 같은 Method를 제공한다.
      • 표현(Representation) : 클라이언트가 서버로 요청을 보냈을 때 응답 자원의 상태를 Representation이라고 한다. REST에서 하나의 자원은 JSON, XML, RSS등 여러 형태의 표현으로 나타낼 수 있다.
    • 필요성

      • 이해하기 쉽고 사용하기 쉬운 API를 제작하는데에 있다.
    • 특징

      1. Unifrom (유니폼 인터페이스)
        • URI로 지정한 리소스에 대한 조작을 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일을 의미
          • URL: 자원이 실재로 존재하는 위치를 가르킴
          • URI: 자원의 위치뿐만이 아니라 자원에 대한 고유 식별자로써 URL 의미를 포함한다.
      2. Stateless (무상태성)
        • 작업을 위한 상태정보를 따로 저장하고 관리하지 않는다. 세션 정보나 쿠키 정보를 별도로 저장/관리 하지 않기 때문에 API 서버는 들어오는 요청만을 단순히 처리하면 된다.
        • 서비스의 자유도가 높아지고, 서버에서 불필요한 정보를 관리하지 않음으로써 구현이 단순해진다.
      3. Cacheable (캐시 가능)
        • HTTP라는 기존 웹 표준을 그대로 사용하기에 웹에서 사용하는 기존 인프라를 그대로 활용하는것이 가능하다. 따라서 HTTP가 가진 캐싱 기능이 적용 가능하다. HTTP 프로토콜 표준에서 사용하는 Last-Modified 태그나 E-Tag를 이용하면 캐싱 구현이 가능하다.
      4. Self-descriptiveness (자체 표현 구조)
        • REST API 메세지만 보고도 이를 쉽게 이해 할 수 있는 자체 표현 구조로 되어있다.
      5. client-server 구조
        • REST 서버는 API 제공, 클라이언트는 사용자 인증 혹은 컨텍스트(세션, 로그인 정보)등을 직접 관리하는 구조로 각각의 역할이 확실히 구분되기에 클라이언트와 서버에서 개발해야 할 내용이 명확해지고 서로간 의존성이 줄어들게 된다.
      6. 계층형 구조
        • REST 서버는 다중 계층으로 구성될 수 있으며, 보안, 로드 밸런싱, 암호화 계층을 추가해 구조상의 유연성을 둘 수 있고 Proxy, 게이트웨이와 같은 네트워크 기반의 중간매체를 사용할 수 있게 한다.
  • RESTful 이란?

    • 정의

      • REST 아키텍처를 구현하는 웹 서비스를 나타내기 위해서 사용되는 용어.
      • REST 원리를 따르는 시스템을 RESTful이라는 용어로 지칭
    • Restful 디자인 가이드

      • URI는 정보의 자원을 표현해야한다.
      GET /members/1
      
      # 리소스는 동사보다 명사를, 대문자보다 소문자를 사용
      # 리소스는 도큐먼트 이름일때 단수 명사를 사용.
      # 리소스가 컬렉션일때 복수 명사를 사용.
      # 리소스가 스토어일때 복수 명사를 사용.
      • 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현
      • URI에 HTTP 메소드가 들어가면 안된다.
      GET /books/delete/1 -> DELETE /books/1
      • URI 행위에 대한 동사 표현이 들어가면 안된다. (CRUD 기능을 나타내는것은 URI에 사용하지 않는다.)
      GET /books/show/1 -> GET /books/1
      GET /books/insert/2 -> POST /books/2
      • 경로 중 변하는 부분은 Unique 값으로 대체한다. (ID는 하나의 특정 resource를 나타내는 고유값을 의미한다.)
      id = 10인 book을 삭제 -> DELETE /books/10
      • / (슬래시 구분자)는 계층관계를 나타낸다.
      http://edwith.org/courses/java
      • URI 마지막 문자로 /를 포함하지 않는다.
      • URI에 포함되는 모든 문자는 리소스 유일한 식별자로 사용되어야 하며, URI가 다르다는것은 리소스가 다르다는 뜻이고, 역으로 리소스가 다르면 URI도 달라져야 한다.
      • 하이픈(-)은 URI 가독성을 높이는데 사용 가능하다.
      • 밑줄(_)은 URI에서 사용하지 않는다.
      • URI 경로는 소문자가 적합하다.
        URI 경로에 대문자 사용은 피하도록 한다. RFC 3986(URI 문법 형식)은 URI 스키마와 호스트를 제외하고는 대소문자를 구별하도록 규정하기 때문이다.
      • 파일 확장자는 URI에 포함하지 않는다. Accept header를 사용하도록 한다.
      http://edwith.org/files/java.jpg (X)
      GET /files/jdk18.exe HTTP/1.1 Host: edwith.org Accept: image/jpg (O)
      • 리소스 간에 연관 관계가 있는 경우 다음과 같은 방법으로 표현한다.
        /리소스명/리소스 ID/관계가 있는 다른 리소스명
      GET : /books/{bookid}/viewers (일반적으로 소유 ‘has’의 관계를 표현할 때)
      • 자원을 표현하는 컬렉션(Collection)과 도큐먼트(Document)

        컬렉션은 객체의 집합, 도큐먼트는 객체라고 생각하면 된다. 컬렉션과 도큐먼트 모두 리소스로 표현할 수 있으며 URI로 표현할 수 있다.

      http://edwith.org/courses/1 
      # courses는 컬렉션을 나타내므로 복수로 표현해야 한다. courses/1 은 courses중에서 id가 1인 도큐먼트를 의미한다.
    • RESTful API를 위한 HTTP Methods

      • GET : 자원을 받아오기만 할 때 사용
        • 어떤 방식으로도 자원을 변경시키지 않기 때문에 safe-method라고도 불린다.
        • 멱등성의 성질을 가지고 있다. 멱등성이란 여러번의 동일한 API를 호출함에도 동일한 결과값을 받음을 의미한다. (POST, PUT을 통해 데이터가 변경되지 않는다면)
      • POST : 새로운 자원이 추가될 때 사용
        • 서버의 상태를 변경시키므로 비멱등성의 성질을 가지고 있다. 성공적으로 추가시 201 (created)를 받는다.
      • PUT : 존재하는 자원을 변경할 때 사용
        • 존재하는 자원이 없으면 새로운 자원을 만들어 내고 응답 코드 201 (created)을 받음.
        • 존재하는 자원이 있으면 200(OK) 또는 204(No Content)를 받음
      • DELETE : 자원을 삭제할 때 사용
        • 지속 삭제 시 서버 상태 변경 X, 하지만 이미 제거되었으므로 응답 코드 404(Not Found)를 반환 받는다.
      • PATCH : 한 자원의 데이터를 부분적으로 변경할 때 사용
        • 조금더 명확하게는 존재하는 자원에 대해 부분적으로 업데이트를 위해서는 PATCH를 사용한다. PUT은 자원을 완전히 대체하는 경우 사용한다.
        • PATCH의 경우 모든 브라우저, 서버, 앱 어플리케이션 프레임워크에서 사용할 수 있는 것은 아니다.

좋은 웹페이지 즐겨찾기