Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

레이어간 데이터 전달 형식 정의 #36

Closed
2 tasks done
junha-ahn opened this issue Jul 17, 2023 · 12 comments · Fixed by #91
Closed
2 tasks done

레이어간 데이터 전달 형식 정의 #36

junha-ahn opened this issue Jul 17, 2023 · 12 comments · Fixed by #91
Assignees
Labels
documentation Improvements or additions to documentation question Further information is requested

Comments

@junha-ahn
Copy link
Member

junha-ahn commented Jul 17, 2023

Description

각 레이어간 데이터 형식 정의

Nodejs에서는(Not nestjs) 응답형식도 정해야했던 기억이 있습니다.

스프링부트에서도 동일한지는 모르겠으나, 각 레이어간, 또는 HTTP Response의 데이터 형식을 통일시켜야함을 분명할 것입니다. (Default가 존재함에도)

자료조사와 형식 정의를 부탁드립니다. (출처 남겨주십시요)

image

참고: PR

#28 (comment)

// as is
return ResponseEntity.ok().body(save)

// to be
return ResponseEntity.body(posted).status(HttpStatus.OK)

참고: Nodejs 경험

하나하나 다 자유로운 Nodejs(?)

Response.js

const Response = (statusStep = 1) => (
  httpCode = 500,
  message = '서버 내부에 장애가 발생했습니다.',
  data = null,
) => {
  return {
    status: (statusStep === 0),
    httpCode,
    message,
    data,
  }
}

To do

  • HTTP Response - (2xx 응답 데이터, 4xx 에러 데이터, custom-message/Type 등 형식 통일)
  • Service => Controller 에러 형식 (즉 레이어 계층별 Error Handling)

그 외)

  • 논의 후 Wiki에 정의
  • http response는 프론트 엔드 개발자의 입장을 고려해주세요
    • . 응답이 요청에 따라 타입이 object, list, text, int 에 대한 parsing 처리
    • 2xx 일때와 4xx일때의 형식이 아예 다르거나 하는 등
// frontend

// Default ?
const response = await axios.get('/book')
{ id: 1, title: '', ...}

const response = await axios.get('/books')
[{ id: 1, title: '', ...}, {...}, {...}]

const response = await axios.post('/book')
1234

// else, Custom 
const response = await axios.get('/book')
{msg: 'success', data: {id:1, title: ''...}}

Test Checklist

  • 개발자는 (앞으로 작성될) 문서를 통해 데이터 형식을 일치시켜 개발할 수 있어야합니다.
  • 또는 해당 문서를 통해 코드 리뷰 할 수 있어야 합니다.
@junha-ahn junha-ahn added documentation Improvements or additions to documentation question Further information is requested labels Jul 17, 2023
@junha-ahn junha-ahn moved this to Todo in Kanban backend Jul 17, 2023
@hihahayoung
Copy link
Collaborator

참고: https://tecoble.techcourse.co.kr/post/2021-05-10-response-entity/

@bohblue2
Copy link
Collaborator

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

/**
 * How to use
 * 1. Make Controller's return type 'ResponseEntity<?>'
 * 2. Use Response.makeResponse(HttpStatus, message, result)
 */

public class Response {
    @Getter
    @Builder
    private static class Body {
        private String message;
        private Object result;
    }

    public static ResponseEntity<?> makeResponse(HttpStatus httpStatus, String message, Object result) {
        Body body = Body.builder()
            .message(message)
            .result(result)
            .build();

        return new ResponseEntity<>(body, httpStatus);
    }

    public static ResponseEntity<?> makeResponse(HttpStatus httpStatus, String message) {
        return makeResponse(httpStatus, message, null);
    }


    // 200
    public static ResponseEntity<?> ok(String message) {
        return makeResponse(HttpStatus.OK, message, null);
    }

    // 201
    public static ResponseEntity<?> created(String message) {
        return makeResponse(HttpStatus.CREATED, message, null);
    }

    // 400
    public static ResponseEntity<?> badRequest(String message) {
        return makeResponse(HttpStatus.BAD_REQUEST, message, null);
    }

    // 401
    public static ResponseEntity<?> noContent(String message) {
        return makeResponse(HttpStatus.NO_CONTENT, message, null);
    }

    // 404
    public static ResponseEntity<?> notFound(String message) {
        return makeResponse(HttpStatus.NOT_FOUND, message, null);
    }

    // 500
    public static ResponseEntity<?> serverError(String message) {
        return makeResponse(HttpStatus.INTERNAL_SERVER_ERROR, message, null);
    }
}

위와 같은 Factory 클래스를 하나 만들어서 처리하는것도 하나의 방법이 될 수 있을 것 같습니다.

@junha-ahn
Copy link
Member Author

junha-ahn commented Jul 17, 2023

참고: try-catch 지옥 벗어나기

  • 점점 Service에서 Controller로 Exception 던지는 경우가 많아질것 같아서 첨부해봅니다.
  • Nodejs에서도 이런 감싸는 Middleware (제 개인적으로는 Container라고 이름 붙여서) try-catch를 한번에 처리했는데... 뭐 스프링 부트도 뭔가 있겠죠?
  • 그러기 위해선 result를 넘기거나 execption을 던지는 어떤 흐름이 통일되어있어야 하겠고요

in nodejs

const container = controller=> async (req, res) => {
  const response = sendResponse(res);
  try {
    const result = await controller(req); // 각각의 controller를 reqeust body를 넣어서 실행하고 응답(RESULT)를 받아옴
    response(result || fail.error.internalError({
      errorMessage: '반환값이 명시되지 않았습니다.'
    }));
  } catch (e) {
    if (config.NODE_ENV !== 'test') console.error(e)
    if (e.status !== undefined) {
      const httpCode = e.httpCode || 500;
      return res.status(httpCode).json(e);
    }
    response(fail.error.internalError({
      errorMessage: e.message || ''
    }));
  }
};;

Spring Boot도 Spring Boot Best practice도 모르니 "nodejs에서는..."만 말하는....

@junha-ahn
Copy link
Member Author

@ParkJeongseop @minjun3021 @yunsuu 이건 모든 분들이 함께 고민해주세요

@minjun3021
Copy link
Collaborator

https://yeonyeon.tistory.com/218

이런식으로 @ExceptionHandler를 이용하여 try-catch를 사용하는게 아닌
Exception 던졌을때 사전에 정해놓은 response 형식으로 자동으로 반환시키고

정상적으로 비지니스 로직이 다 돌아갔을때는 Response 클래스 만들어놓은거 반환하면 좋을거 같습니다~

@junha-ahn
Copy link
Member Author

junha-ahn commented Jul 17, 2023

@bohblue2

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

위와 같은 Factory 클래스를 하나 만들어서 처리하는것도 하나의 방법이 될 수 있을 것 같습니다.

레퍼런스 또는 해당 패턴에 대한 키워드 같이 주시면 좋을 것 같습니다!

@junha-ahn
Copy link
Member Author

참고로 해당 이슈는 바로 각 PR에 적용되는게 아니라, 요번주간 논의 후 향후 통합 작업때 적용하면 될 것 같습니다

  • 바로 현재 개발 중인 PR에 적용할 필요 없습니다~

@junha-ahn
Copy link
Member Author

junha-ahn commented Jul 19, 2023

참고

에러 핸들링, DTO 관련된 내용은 잘 모르겠네요

@junha-ahn junha-ahn moved this from Todo to In Progress in Kanban backend Jul 21, 2023
@junha-ahn junha-ahn moved this from In Progress to Todo in Kanban backend Jul 23, 2023
@junha-ahn junha-ahn mentioned this issue Jul 23, 2023
3 tasks
@bohblue2
Copy link
Collaborator

bohblue2 commented Jul 23, 2023

CustomResponse 클래스 구조 입니다.

{
    status: success or failed, // success: 성공, failed: 에러 
    message: empty or error message,
    data: [] || null || data,

    error_code: 0(success) or 사용자 정의 에러 코드 사용
    errors: [] || null, (validation- error) 
}

@junha-ahn
Copy link
Member Author

junha-ahn commented Jul 23, 2023

좋습니다.

API Response 고려사항으로는 사실 #40 도 있습니다.

  • Validator로 Sprnig Boot에서 제공하는 특정 라이브러리/패키지를 사용할탠데, 해당 라이브러리가 뿜어내는 Http Response 형식도 고려해야하죠.
  • 일단 위 내용을 base로 계속 수정/보완하면 좋을 것 같습니다.

HTTP 커스텀 에러 코드 관련 참고


일단 HTTP Response는 Validator 정의까지 일단 멈추고, 전체 레이어 Error Handling 방식 정의 해주시면 좋을 것 같습니다.

@junha-ahn
Copy link
Member Author

junha-ahn commented Aug 5, 2023

Wiki에 HTTP Response에 대한 정리 해놨습니다.

  • 어떻게 해당 format으로 (중복없이) 모든 Response를 통일할지는 고민해봐야겠네요

그리고 @Requestbody 어노테이션을 사용하면 application/json외에는 모든 요청을 거절한다고 하네요.

@ExceptionHandler를 이용해서 Custom Error 를 던지면 될것 같군요...

@junha-ahn junha-ahn moved this from Todo to In Progress in Kanban backend Aug 28, 2023
@junha-ahn junha-ahn moved this from In Progress to Todo in Kanban backend Aug 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation question Further information is requested
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

6 participants