본문 바로가기
Web Technologies 🖥️/etc

API 응답 구조화 (feat... 유틸 클래스)

by dudefromkorea 2024. 3. 8.

API 서버를 개발할 때

클라이언트와 원활한 소통을 위해

일관된 응답 구조 방식을 채택할 수 있다

 

API 엔드포인트에서

성공 여부와 에러를 전달할 때

일정한 포멧으로 응답을 주고 받으면

클라이언트에서 예측 가능한 처리가 가능하다

 

유틸 클래스의 필요성

API의 갯수가 많아질수록 동일한 형태의

성공 / 실패 응답을 자주 사용하게 될 가능성이 높다

이는 자연스래 중복된 코드로 이어지고

유지보수 또한 까다로워진다

 

동일한 엔드포인트에서

성공 / 실패 여부에 따른 응답을

위와 같이 설정할 수 있다

성공 시 응답 데이터

{
  "result": true,
  "errNum": null,
  "data": {
    "message": "Registration Success"
  }
}

 

실패 시 응답 데이터

{
  "result": false,
  "errNum": 1001,
  "data": {
    "message": "Invalid Credentials"
  }
}

 

이러한 응답 구조를 관리하기 위해

공통된 형태의 응답을 반환하는

유틸 클래스를 설계할 수 있다

BuzzTalkResult 유틸클래스

/*
* 성공 시 success 메소드
* 실패 시 resultError 메소드 에러 상태 코드(errNum)와 에러 메시지를 errorMsg 객체에 매핑
* 예기치 못한 오류 발생시 handleError 메소드 호출
*/

export class BuzzTalkResult {
  private errorMessages: { [key: number]: string } = { // 에러 메시지를 상태 코드와 매핑
    1000: 'Internal Server Error',
    1001: 'Invalid Credentials',
    1002: 'Player Not Found',
    1003: 'Password Incorrect', // etc ......
  };

  response: { [key: string]: any } = {};

  resultError(errNum: number): { [key: string]: any } {
    // 에러 메시지가 없으면 'Unknown Error' 반환
    const message = this.errorMessages[errNum] || 'Unknown Error';  
    return this.result(false, errNum, { message });
  }

  success(data: any): { [key: string]: any } {
    return this.result(true, null, data);
  }

  result(chk: boolean, errNum: number | null, data: any): { [key: string]: any } {
    const map: { [key: string]: any } = {};
    map['result'] = chk;
    map['errNum'] = errNum; // 오직 에러 메세지 기반 응답 처리 방지 
    map['data'] = data;
    return map;
  }

  handleError(e: any): { [key: string]: any } {
    console.error('Error occurred:', e);
    return this.resultError(20);  // 기본적으로 1000번 코드로 처리 (Internal Server Error)
  }
}

 

유틸클래스를 위와 같이 설계하였을 경우

API에서는 아래와 같이 활용 가능하다

회원가입 API

@Controller('/player')
export class PlayerController {
  constructor(
    private readonly playerService: PlayerService, 
    private readonly buzzTalkResult: BuzzTalkResult
  ) {}

  @Post('/register')
  async register(@Body() body): Promise<any> {
    try {
      const playerId = body.playerId;
      const password = body.password;
      
      const statusCode = await this.playerService.register(playerId, password);
      
      if (statusCode === 0) {
        return this.buzzTalkResult.success({ message: 'Registration Success' });
      }
      return this.buzzTalkResult.resultError(statusCode);  // 실패 응답 처리
    } catch (e) {
      return this.buzzTalkResult.handleError(e);  // 예외 발생 시 처리
    }
  }
}

 

 

유틸 클래스의 단점

유연성 부족

모든 API가 동일한 응답으로

처리하도록 설계되어있기 때문에

특정 API에서 더 복잡하거나

특정 응답 구조가 필요할 경우 제한적이다

 

클래스 비대화

다양한 타입의 응답 처리가 필요할 경우

(ex: 파일 다운로드 링크)

클래스가 계속 확장하며 관리가 어려워질 수 있다

728x90
반응형

'Web Technologies 🖥️ > etc' 카테고리의 다른 글

API 명세서 자동화 - swagger (feat. express.js)  (0) 2024.10.10