Java

[어드민 페이지 만들기] 헤더(Header) 파일 정의하기, CRUD 인터페이스 만들기

15호의 개발자 2022. 3. 14. 12:00
반응형

[어드민 페이지 만들기] 헤더(Header) 파일 정의하기, CRUD 인터페이스 만들기

 


 

response sample

 

위 response sample에서 노란색으로 칠해져있는 박스기본적으로 들어가는 정보들이 들어있는 부분이다. 

  • transaction_time: 해당 통신이 일어난 시간
  • result_code: 해당 API에 대한 응답
  • description: 설명

 

물론 result_code의 경우, http status code로 조절하는 경우도 있지만, 정상적/비정상적인 통신이나 권한을 제외하고 일반적으로 통신이 되었을 때, 직접 정의한 에러 코드를 내려주기 위해 result_code를 따로 정의한 것이다.

 

녹색으로 칠해져 있는 박스데이터에 해당하는 부분으로, 매번 바뀌는 값이다. 

 

예를 들어, 사용자를 조회할 때와 상품을 조회할 때를 생각해보자. 두 경우 모두 노란색 부분은 존재할 것이고 초록색 부분이 매번 값이 달라질 수 있는 부분이다.

 

따라서, 같은 response body 부분이지만 노란색 부분(언제나 같은 값으로 내려가는 부분)헤더라고 부를 것이고, 초록색 부분(데이터 부분)은 항상 값이 바뀌기 때문에 데이터 또는 바디라고 부르기로 하자. 

 

그럼 이제부터 헤더(노란색 부분)를 어떻게 개발하는지 알아보자.

 


1. 헤더

 

이제 네트워크에 해당하는 부분을 코딩해볼 것이다. 나의 경우 기존에 entity가 들어있던 model 패키지 하위에 network 패키지를 추가로 만들었다. 그 아래에 Header 클래스를 생성한다. 우선 항상 같은 값으로 내려가는 부분인 헤더 부분을 아래와 같이 정의할 수 있다.

 

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Header {

    // api 통신시간
    private LocalDateTime transactionTime;

    // api 응답코드
    private String resultCode;

    // api 부가설명
    private String description;

}

Header 파일에는 항상 들어가는 값인 api 통신시간, api 응답코드, api 부가설명을 내려주는 값을 작성한다. 참고로, api 통신시간의 경우 LocalDateTime를 사용하긴 했지만, 실제 프론트단에 보내줄 때는 String으로 더 많이 보내준다. 그렇게 해야 형 변환하기 더 쉽기 때문이다.

 

 

지난 시간에, get을 통해서 object를 내리게 된다면 JSON 형식으로 변환을 해주도록 코드를 작성했었다. 예를 들어 SearchParam을 쓰는 경우, get을 통해 object를 리턴하게 되면 잭슨 라이브러리를 통해 자동으로 JSON 형식으로 변환을 해준다. (ex. {"account": "", "email": "", "page": 0})

 

아래 글 "2) JSON 형식으로 리턴" 참고

 

[Spring Boot] Get 메소드 사용법 (@RequestMapping, @GetMapping, 멀티 파라미터, JSON 리턴)

[Spring Boot] Get 메소드 사용법 (@RequestMapping, @GetMapping, 멀티 파라미터, JSON 리턴) @RequestMapping import org.springframework.web.bind.annotation.*; @RestController // controller임을..

unit-15.tistory.com

 

 

이와 같이 이번엔 get 컨트롤러에 Header가 잘 내려갔는지 확인할 수 있는 코드를 작성해보자.

 

@RestController // controller임을 알려주는 표시
@RequestMapping("/api") // 이곳으로 들어오는 API주소를 mapping, /api주소로 받겠다(localhost:8080/api)
public class GetController {

    @GetMapping("/header")
    public Header getHeader() {

        // {"resultCode": "OK", "description": "OK"}
        return Header.builder().resultCode("OK").description("OK").build();
    }

}

 

Header라는 object를 반환할 것이며, resultCode와 description이 아래와 같이 "OK" 값을 가진 JSON 형태로 내려갈 것이다.

{"resultCode": "OK", "description": "OK"}

 

 

이를 확인하기 위해 지난 시간에 이용했던 Rest Client 툴인 Talend API Tester를 이용해서 해당 JSON 값을 확인해보자.

 

아래 글 "Rest Client 툴을 이용한 테스트" 참고

 

[Spring Boot] Get 메소드 사용법 (@RequestMapping, @GetMapping, 멀티 파라미터, JSON 리턴)

[Spring Boot] Get 메소드 사용법 (@RequestMapping, @GetMapping, 멀티 파라미터, JSON 리턴) @RequestMapping import org.springframework.web.bind.annotation.*; @RestController // controller임을..

unit-15.tistory.com

 

 

Talend API Tester에 들어가서 아래 주소로 get 메소드를 요청한다.

 

 

정상적으로 응답이 오면 Response값에 200 OK가 뜨면서, 바디(BODY) 부분에는 resultCode와 description이 우리가 입력했던 "OK"가 잘 들어가있는 것을 확인할 수 있다. transactionTime은 입력하지 않았으므로 null 값으로 되어있다.

 


2. 데이터(또는 바디)

 

그러면 이제 데이터 부분, 즉 계속해서 바뀌는 바디 부분을 어떻게 정의하는지 알아보자. 여러 방법이 있겠지만 일반적으로 사용하는 방법은 generic(제네릭)을 이용하는 방법이다. 아까 만들었던 Header 클래스에 제네릭을 추가한다.

 

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Header<T> {

    // api 통신시간
    private String transactionTime;

    // api 응답코드
    private String resultCode;

    // api 부가설명
    private String description;

    private T data;

}

계속 바뀌는 data 부분을 정의할 때 제네릭(<T>)을 이용한다. 제네릭을 통해 언제든 몸통을 갈아끼울 수 있다.

 

이에 덧붙여 정상적인 통신인 경우에 사용하는 OK 함수와, 에러가 났을 때 사용하는 ERROR 함수를 아래와 같이 만든다. OK 함수는 데이터가 있는 경우와 없는 경우로 나눠서 만들었다.

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Header<T> {

    // api 통신시간
    private String transactionTime;

    // api 응답코드
    private String resultCode;

    // api 부가설명
    private String description;

    private T data;

    // OK   (정상적인 통신일 때는 OK만 호출)
    public static <T> Header<T> OK() {
        return (Header<T>)Header.builder()
                .transactionTime(LocalDateTime.now())
                .resultCode("OK")
                .description("OK")
                .build();
    }

    // DATA OK  (데이터가 있을 때는 DATA OK 호출)
    public static <T> Header<T> OK(T data) {    // 제네릭으로 data를 받음
        return (Header<T>)Header.builder()
                .transactionTime(LocalDateTime.now())
                .resultCode("OK")
                .description("OK")
                .data(data)
                .build();
    }

    // ERROR    (비정상일 때는 ERROR 호출)
    public static <T> Header<T> ERROR(String description) {
        return (Header<T>)Header.builder()
                .transactionTime(LocalDateTime.now())
                .resultCode("ERROR")
                .description(description)   // 어떤 에러인지 프론트엔드에게 알려줌
                .build();
        }
        
}

 


 

cf. 카멜 케이스에서 스네이크 케이스로 변환

 

일반적으로 api를 작성할 때는 snake case로 작성하는 것이 정석이지만, 나의 경우 camel case로 내려주고 있다. 따라서 이 부분을 바꿔줘야 한다. 방법은 여러 개가 있다. 아래와 같이 @JsonProperty로 직접 바꿔줄 수도 있다.

 

@JsonProperty("transaction_time")
private String transactionTime;

 

하지만 변수가 많은 경우 이를 일일이 지정해주기는 번거로우므로 application.properties에 아래 설정을 추가함으로써 간편하게 처리할 수 있다. properties에 추가한 뒤에는 프로젝트를 다시 실행해준다.

 

spring.jackson.property-naming-strategy=SNAKE_CASE

 

그러면 response body 데이터가 아까와는 다르게 snake case로 변환된 것을 확인할 수 있다. properties에 스네이크 케이스로 변환해주는 jackson 설정을 추가해줌으로써, 개발할 때는 카멜 케이스로 개발을 하고 api를 주고받을 때는 스네이크 케이스로 주고받을 수 있게 되었다.

 

 


3. CRUD 인터페이스 만들기

 

이제 본격적으로 Controller를 생성해야한다. API 컨트롤러와 Page 컨트롤러를 나누기 위해 컨트롤러 하위에 api와 page 패키지를 만든다.

 

 

@RestController
@RequestMapping("/api/user")
public class UserApiController {

    public Header create() { } // 임시
    
    public Header read() { } // 임시
    
    public Header update() { } // 임시
    
    public Header delete() { } // 임시
    
}

우선 UserApiController에 해당 컨트롤러는 RestController이며, /api/user 의 주소를 갖는다는 것을 선언해준다.

 

그리고 이제 CRUD 메서드를 하나씩 작성해야 한다. 하지만 User 컨트롤러 말고도 만들어야 할 컨트롤러가 많다. 이렇게 컨트롤러를 계속 생성하다보면 CRUD 메서드를 놓치거나 잘못 작성할 가능성이 높아진다. 따라서 인터페이스를 만들어서 CRUD 메서드는 반드시 재정의해야 한다고 강제시키도록 하자.

 

ifs 패키지를 하나 만든 후 그 하위에 CrudInterface라는 인터페이스를 하나 만들었다.

 

import com.example.study.model.network.Header;

public interface CrudInterface { // 반드시 작성해야할 부분을 이렇게 인터페이스로 만듦

    Header create(); // todo request object 추가

    Header read(Long id);

    Header update();

    Header delete(Long id);

}

 

create나 update에 받을 파라미터는 지금은 일단 비워두고 다음 시간에 추가하기로 한다.

 

@RestController
@RequestMapping("/api/user")
public class UserApiController implements CrudInterface {

    @Override
    @PostMapping("")    // /api/user
    public Header create() {
        return null;
    }
    
    @Override
    @GetMapping("{id}") // /api/user/{id}
    public Header read(@PathVariable(name = "id") Long id) {
        return null;
    }
    
    @Override
    @PutMapping("") // /api/user
    public Header update() {
        return null;
    }
    
    @Override
    @DeleteMapping("{id}")  // /api/user/{id}
    public Header delete(@PathVariable Long id) {
        return null;
    }
    
}

 

다시 UserApiController에 가서 방금 만든 CrudInterface를 상속받아준다. 아까 임시로 만들어뒀던 CRUD 메서드들을 다 지우면, 인텔리제이든 이클립스든 override를 자동으로 해주는 기능을 이용한다. 

 

위 코드는, 앞으로 어드민 페이지 만들기 프로젝트에서 사용하는 CRUD를 아래의 규칙대로 Http 메소드와 매칭 시키겠다는 의미이다.

 

  1. create에 대해서는 반드시 PostMapping으로 받는다.
  2. read에 대해서는 GetMapping으로 받을 것이며, PathVariable을 이용한다.
  3. update에 대해서는 PutMapping으로 받는다.
  4. delete에대해서는 DeleteMapping으로 받으며, PathVariable을 이용한다.

 


 

 

 

(출처: 패스트캠퍼스 Java & SpringBoot로 시작하는 웹 프로그래밍)

 

반응형