Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

API 구현 예시와 팁 본문

Spring/Spring 기반 REST API 개발

API 구현 예시와 팁

studyHub 2024. 10. 22. 22:25

# 테스트할 것

JSON 응답이 201로 나오는지

입력 값 제한

 

# 구현 코드

@MockBean으로 repository Mock 객체를 만들어 쓰다가, 실제 DB에서 Id값을 반환해줘야해서 @Springboot 통합 테스트로 전환

 

API 응답 컨트롤러

@RestController

  • @ResponseBody를 모든 메소드에 적용한 것과 동일하다

ResponseEntity를 사용하는 이유

  • 응답 코드, 헤더, 본문 모두 다루기 편한 API

객체를 JSON으로 변환

  • ObjectMapper 사용

DTO를 Entity로 변환

  • ModelMapper 사용 (Bean으로 등록해야 함)
@Configuration
public class AppConfig {

    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }

 

입출력 값 제한

입력값 제한 - DTO와 ModelMapper 활용

  • id 또는 입력 받은 데이터로 계산해야 하는 값들은 입력을 받지 않아야 한다.
  • EventDto 적용 (feat. ModelMapper)
    • DTO -> 도메인 객체로 값 복사를 위해 ModelMapper 라이브러리 추가

추가한 ModelMapper를 빈으로 등록
ModelMapper는 기존 객체를 덮어씌우거나, 새로운 객체를 만들어준다

  • 입력 허용 값 이외에 파라미터가 들어올 경우 에러 발생
    • ObjectMapper 커스터마이징하여 JSON->DTO 변환 시 unknown properties가 있으면 에러 발생하도록 설정
      spring.jackson.deserialzation.fail-on-unknown-properties=true

 

출력값 제한 - JsonSerializer

Item을 조회 API 응답 시, 아래와 같이 기본적으로 Item 생성자인 Account 엔티티의 정보가 전달되는 심각한 문제가 있다.

"manager":{
            "id":2,
            "email":"user@email.com",
            "password":"{bcrypt}$2a$10$lheCQFmVWN0sfQumXoJbguajF7Ag8gYbiusnGgRs2EZvRnUUOr/ia",
            "role":"USER"
            }

 

아래와 같이JsonSerializer로 JSON으로 직렬화하는 데이터를 정의해주면 된다.

import com.fasterxml.jackson.databind.JsonSerializer;

public class AccountSerializer extends JsonSerializer<Account> {
    @Override
    public void serialize(Account account, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
        gen.writeStartObject();
        gen.writeNumberField("id", account.getId());
        gen.writeEndObject();
    }
}

 

그리고 Item 엔티티의 필드에 적용할 Serializer를 정의해준다.

@Entity
public class Event {

    @Id
    @GeneratedValue
    private Integer id;
    
    @JsonSerialize(using = AccountSerializer.class)
    private Account manager;
}

 

만약 글로벌하게 적용하고 싶다면, @JsonComponet를 붙이면 된다.

@JsonComponent //Springboot 가 사용하는 ObjectMapper 에 등록
public class ErrorsSerializer extends JsonSerializer<Errors> {
    @Override
    public void serialize(Errors errors, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
        gen.writeFieldName("errors");
        gen.writeStartArray();
        errors.getFieldErrors().forEach(e -> {
            try {
                gen.writeStartObject();
                gen.writeStringField("field", e.getField());
                gen.writeStringField("objectName", e.getObjectName());
                gen.writeStringField("code", e.getCode());
                gen.writeStringField("defaultMessage", e.getDefaultMessage());
                Object rejectedValue = e.getRejectedValue();
                if (rejectedValue != null) {
                    gen.writeStringField("rejectedValue", rejectedValue.toString());
                }
                gen.writeEndObject();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        });

        errors.getGlobalErrors().forEach(e -> {
            try {
                gen.writeStartObject();
                gen.writeStringField("objectName", e.getObjectName());
                gen.writeStringField("code", e.getCode());
                gen.writeStringField("defaultMessage", e.getDefaultMessage());
                gen.writeEndObject();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        });
        gen.writeEndArray();
    }

 

 

입력 값 검증

  • @Valid와 BindingResult (또는 Errors)
  • 도메인 Validator 만들기 ( Validator 인터페이스 없이 만들어도 상관없음)
  • 생성된 error를 return하면, error를 json으로 변환할 때 springboot가 사용하는 objectMapper는 java bean spec이 안맞아서 변환이 안되어 오류가 발생한다. 따라서 objectMapper가 사용할 수 있는 커스텀 JSON Serializer를 만들어 등록

 


참고 자료 & 이미지 출처
스프링 기반 REST API 개발 (백기선 님)