kakasoo

모든 타입이 추론되는 API 만들기 ( feat. Nestia ) 본문

프로그래밍/NestJS

모든 타입이 추론되는 API 만들기 ( feat. Nestia )

카카수(kakasoo) 2023. 3. 26. 23:08
반응형

이 글은 다음의 내용을 다룬다.

  • 프론트 개발자들이 눈치채면 곤란한 백엔드 이야기
  • 백엔드 개발자가 프로그래밍 언어가 Node.js 라서 얻을 수 있는 이점
  • Nestia 라이브러리를 이용한 Swagger 문서 및 SDK 생성
  • 에러를 던지지 않고 값으로 다루는 방식

 

서버와 프론트가 분리되서 발생하는 문제들

첫째, 서버와 프론트의 중복 개발?

프론트 : 이거 API 호출하면 어떤 결과가 와요?

프론트 개발자가 질문을 한다.

이 질문에 대해서는 응답과, 각 요청 파라미터에 따른 에러 케이스들, 에러일 때 던지는 값들을 말해줘야 한다.

그러면 그 설명을 듣고 프론트 개발자는 그 응답에 맞는 타입 ( JS 라면 값 ) 을 정의해둘 것이다.

이는 Node.js 백엔드 개발자 입장에서는, 백엔드에서 이미 개발한 것들을 중복 개발하는 것처럼 보일 것이다.

 

백 : ( 아니, 이거 내가 타입 정의해뒀는데 그냥 가져다가 쓰라고 해도 되지 않을까? )

훌륭한(?) Node.js 백엔드 개발자라면, 그 타입들을 npm 이나 다른 레포지토리에 올려서 공유해줄 것이다.

그러면 프론트 개발자는 API를 호출할 때 그 타입들을 명시하여, 어떤 응답이 올지를 예측할 수 있게 된다.

하지만 기획부터 디자인, 개발까지의 잦은 수정은 이런 API를 공유하기 어렵게 한다.

그리고 무엇보다도,

 

백 : ( 어차피 고생은 프론트 개발자가 할 건데 굳이 공유해줘야 하나? 나도 귀찮은데? )

모든 백엔드 개발자가 이렇게 생각하지는 않겠지만, ( 그리고 않을 거라고 믿지만 )

API가 업데이트될 때마다 그 spec을 공유하는 것은 결코 쉬운 일이 아니다.

1회성에 그친다고 하더라도, 어떤 API가 어떤 응답을 주는지를 타입으로 공유하는 건 긴 커뮤니케이션이 필요하다.

백엔드 입장에서는 이 과정을 굳이 자기가 나서서 하고 싶지 않을 것이다.

백엔드 개발자가 서버에서 타입이 정의된 위치를 알려주기만 해도 양반일 것이다.

 

둘째, try-catch문의 문제점

try-catch문은 에러를 던질 때, 에러가 캐치되기 전까지 에러가 함수의 최상위 스택까지 한 번에 이동한다.

이는 쉽고 직관적으로 보일 수 있지만 코드가 길어지면 길어질수록 에러 발생 원인을 파악하기 어려워진다.

try-catch문의 문제점은 다음과 같다.

  1. 예외 처리를 너무 남발하면 코드가 지나치게 복잡해질 수 있다.
  2. catch문 내에서 잡힌 에러를 제대로 처리하지 않으면 원래의 문제를 해결하지 못하며, 파악이 더 어려워진다.
  3. 함수의 정의만 보고는 이 함수가 어떠한 에러를 던질 것인지에 대해서 파악하기가 어렵다.

그래서 만약 에러를 던지지 않고 정해진 형태의 객체로써 다룬다면 어떨까? 라는 생각을 하게 된다.

그러면 잘 만들어진 API 함수에 마우스만 올려놔도 그 API의 리턴 타입을 통해 호출 결과를 예상할 수 있다.

하지만 사실 이렇게 하든 안 하든 프론트를 개발하는 입장에서는 큰 차이가 없었을 것이라고 생각한다.

어차피 프론트 입장에서는 API의 호출 결과는 전부 any 타입인데?

 

셋째, 수정사항을 알기 힘든 환경

A가 B의 코드를 참조해서 사용한다.

B가 코드를 수정하면 A의 코드는 빨간색 밑줄이 생기며 컴파일 에러가 생기며 B는 뭔가 달라졌음을 눈치챈다.

하지만 코드가 분리된 서버와 프론트 코드에서는 서버가 무언가를 바꿔도 프론트가 알아챌 수가 없다.

그러면 이제, 서버가 수정사항을 깜빡하고 말하지 않는 경우에 문제가 생길 수 밖에 없다.

 

프론트 : 어? 이거 분명 되던 건데 왜 갑자기 안 되지? 아, 뭐가 문제야...
백 : ( 어? 저거 혹시 내가 고친 거 때문에 그런가? )

물론 백엔드 개발자가 테스트 코드를 잘 짜두면 이 문제는 방지할 수 있을 것이다.

하지만 API의 스펙 자체가 바뀌는 경우, 연동이 잘 되었는지 매 순간 확인해주는 것도 상당한 수고가 든다.

 

서버 API 연동하기

백엔드 개발자가 API를 개발하고 나면, 그 다음 연동 과정을 거쳐야 한다.

원래대로면 프론트 개발자는 fetch나 axios를 사용해서 그 기능을 하나 하나 연동해야 한다.

그런데,

  1. 백엔드 개발자 입장에서는 이 문서화 작업은 너무나 지루한 작업인 데다가, 여기서도 휴먼 에러가 날 수 있고,
  2. API를 연동해도 그 리턴 타입은 any이기 때문에 swagger나 wiki를 보고 하나 씩 맞춰봐야 하며,
  3. 이후에 API가 수정될 시에 커뮤니케이션이 잘 이루어지지 않았다면 프론트에서는 수정 사항을 파악 못한다.
  • “어라?, 이거 API 수정한 거 있어요?” 라는 말을 자주 듣는다면…

소프트웨어 개발은 다 만들었다고 땡이 아니라, 만든 이후에도 계속 해서 해야 할 일들이 있다.

사실 이러한 과정은 백엔드 개발자가 이미 한 일에 대해서 프론트 개발자가 중복 개발하는 내용에 해당한다.

  • 이미 백엔드 개발자가 API를 만들었는데 왜 그걸 문서로 정리해야 하는가?
  • 백엔드 코드에서 이미 타입을 전부 명시했는데 왜 프론트에선 리턴 타입을 모르는가?
  • 수정 사항이 생긴 걸 왜 발견하지 못하는가?

여기서 발생하는 대부분의 문제는 백엔드 언어가 Node.js면 사실은 해결할 수 있는 것이다.

 

Nestia를 활용한 Swagger 문서 및 SDK 생성

npx nestia swagger & npx nestia sdk

Nestia를 사용하게 되면, 백엔드 개발자가 API 코드를 작성한 즉시 문서와 SDK를 생성할 수 있다.

그것도 백엔드 개발자가 API를 만들 때 지정한 파라미터와 리턴 타입만 가지고도.

그러면 이제 이 SDK를 npm에 private로 배포해서 프론트 개발자보고 install 하라고 해주기만 하면 된다!

프론트 개발자는 그럼 install한 서버 APIs로부터 아래와 같이 코드를 작성할 수 있다.

/**
 * 서버에서 api/articles 경로에 write() 라는 함수명으로 POST API를 작성한 경우
 */

import * as APis from 'serverApis/api/functional';

/**
 * 서버의 API 경로를 점 연산자(.)로 표현하며, 마지막 API 호출 함수는 서버의 함수 명과 동일
 * 자동 완성 기능으로 경로부터 함수 명까지 모두 추론되고,
 * 서버가 API에 남긴 모든 타입과 주석이 프론트 개발자에게 그대로 공유된다.
 */
const article = await Apis.api.articles.write({
    host: process.env.HOST,
}, {
    title: 'title',
    contents: 'contents',
})

아래 링크에서는 Nestia 적용 방법에 대해서 다루고 있다.

Nestia 적용하기 를 차례대로 따라하면 Nestia 적용이 가능하다.

Nestia를 사용하고 나면 이제 프론트 개발자와의 커뮤니케이션 방법도 달라질 수 밖에 없다.

 

Nestia를 활용하면 달라지는 점

응답 형태에 관하여

// 일반적인 협업 상황

프론트 : 게시글 작성하는 API 하면 리턴 타입이 어떻게 와요?
백 : ( 아, 그거 스웨거 문서에 표시 안했던가? )
// Nestia를 사용했을 때의 협업 상황

프론트 : Apis.articles.write()의 리턴 타입은, 어디 보자...
백 : ( API를 만들었으면 그거에 대해 설명할 필요가 없네 )

Nestia를 사용하면 NestJS 코드를 사용해서 프론트 개발자가 즉시 사용 가능한 형태의 SDK를 만들 수 있다.

즉, 백엔드 입장에서 응답에 대해서 하나 하나 설명해줄 필요가 없다.

 

수정 사항에 관하여

// 일반적인 협업 상황 1

프론트 : 어 이거, 어제까지 되던 건데 갑자기 안 되는데 뭐 바꾼 거 있어요?
백 : 어떤 API 말하는 거에요?

// 일반적인 협업 상황 2
프론트 : ( API가 수정되어서 정상적으로 동작하지 않는 상황이지만 변경점을 파악하지 못했다. )
백 : ( 변경점을 프론트에게 말해주었다고, 잘못된 기억을 가지고 있다. )
// Nestia를 사용했을 때의 협업 상황

백 : API 새로 배포했으니깐 npm install 다시 해주세요~
프론트 : 어, install 하고 나니깐 빨간 줄이 생기네요... 타입이... 바뀌었구나. 어디 보자...
백 : ( 내가 도와드릴 필요는 없겠네. )

그 코드를 가져다 쓰는 프론트가, 작업 전 버전만 맞춰주면 된다.

그러면 수정사항이 발생할 경우 컴파일 에러가 발생하기 때문에 프론트 개발자는 배포 전 문제를 파악할 수 있다.

하지만 Nestia에서도 아쉬운 점이 하나 있다면, 에러의 경우에는 추적이 불가능하다는 점이다.

예를 들어, 어떤 SNS 서비스에서 유저가 자기 자신을 팔로우한다면 어떻게 되는가?

만약 이걸 예외처리해주고 있다면 함수의 spec만 보고 어떤 에러가 나올지를 예측하는 것은 불가능해진다.

이 문제를 해결하기 위해 에러를 값으로써 다루면 어떨까?

 

에러를 값으로 다루기

export interface ResponseForm<T> {
  result: true;
  code: 1000;
  data: T;
}
export interface ErrorResponse {
  result: false;
  code: number; // 4000 ~ 4999까지의 수를 할당할 예정
  data: string; // 에러 응답의 경우 data는 에러 메시지가 담길 예정
}

이번에는 에러를 값으로 다루기 위해서, 일단 에러의 형태를 다음과 같이 정의했다.

보다시피 ErrorResponse는 내가 API에서 반환하는 응답의 형태와 유사하다.

result는 응답을 정상과 에러를 구분하기 위한 값이며, code도 마찬가지지만 에러의 경우 1000이 아닌 수다.

400이라는 statusCode를 사용하지 않을 것이기에, 4000부터 4999까지의 수를 할당할 예정이다.

마지막 data는 정상적인 경우에는 응답하려던 값을, 에러 상황에서는 에러 메시지를 담을 예정이다.

즉, 응답과 에러의 형태는 프로퍼티의 값만 다룰 뿐 동일한 형태를 띄게끔 만들었다.

따라서 하나의 에러를 정의한다면 이렇게 작성할 수 있다.

 

const ALREADY_CREATED_EMAIL: ErrorResponse = { 
  result: false,
  code: 4001,
  data: '이미 생성된 이메일입니다.'
};
export const ERROR: Record<string, ErrorResponse> = {
  ALREADY_CREATED_EMAIL: { result: false, code: 4001, data: '이미 생성된 이메일입니다.' },
  NO_AUTH_TOKEN: { result: false, code: 4002, data: '인증이 필요합니다.' },
  IS_SAME_POSITION: { result: false, code: 4003, data: '이미지의 정렬 값이 동일한 경우가 존재합니다.' },
  CANNOT_FINDONE_ARTICLE: { result: false, code: 4004, data: '게시글을 찾지 못했습니다.' },
  SELECT_MORE_THAN_ONE_BODY_IMAGE: { result: false, code: 4005, data: '적어도 1장 이상의 이미지를 골라야 합니다.' },
  NOT_FOUND_ARTICLE_TO_COMMENT: { result: false, code: 4006, data: '댓글을 작성할 게시글을 찾지 못했습니다.' },
  TOO_MANY_REPORTED_ARTICLE: { result: false, code: 4007, data: '신고가 접수된 게시글이라 댓글 작성이 불가능합니다.' },
  ALREADY_FOLLOW_USER: { result: false, code: 4008, data: '이미 좋아요를 누른 유저입니다!' },
  CANNOT_FIND_ONE_DESIGNER_TO_FOLLOW: { result: false, code: 4009, data: '팔로우할 유저를 찾지 못했습니다.' },
  STILL_UNFOLLOW_USER: { result: false, code: 4010, data: '아직 팔로우한 적 없는 유저에요!' },
  CANNOT_FIND_ONE_DESIGNER_TO_UNFOLLOW: { result: false, code: 4011, data: '언팔로우할 유저를 찾지 못했습니다.' },
  CANNOT_FIND_ONE_REPLY_COMMENT: { result: false, code: 4012, data: '답글을 달 댓글을 찾지 못했어요.' },
  ALREADY_CREATED_PHONE_NUMBER: { result: false, code: 4013, data: '이미 생성된 전화번호입니다.' },
  ARLEADY_REPORTED_ARTICLE: { result: false, code: 4014, data: '이미 신고한 게시글입니다.' },
  IS_NOT_WRITER_OF_THIS_ARTICLE: { result: false, code: 4015, data: '이 게시글의 작성자만이 수정할 수 있습니다.' },
  CANNOT_FIND_ONE_COMMENT: { result: false, code: 4016, data: '해당 댓글을 찾지 못했습니다.' },
  CANNOT_FOLLOW_MYSELF: { result: false, code: 4017, data: '설마 자기 자신을 팔로우하려고 했어요?!' },
} as const;

에러가 많아지면 관리하기 힘들기 때문에 이처럼 에러를 한 객체에 모아서 관리해주기로 했다.

이 객체는 에러의 이름과 그 에러 이름에 해당하는 ErrorResponse를 명시해주기만 하면 된다.

따라서 이 에러들을 모아놓은 객체의 타입은 Record<string, ErrorRespnose>가 된다.

 

23.03.29
- "as const" 가 반드시 필요한데 누락했다.

- 에러를 계층 별로 구조화하는 게 낫다는 피드백을 받음.

 

23.04.09

- Nestia에서 typeof 사용 금지

- Nestia에서 namespace를 사용할 경우 SDK 생성에 문제가 발생하는 경우가 있음.

- 자세한 내용은 아래 글을 참고할 것 ( 해결 방법 추가 )

 

Nestia에서 typeof, namespace를 쓰면 안 됩니다

왜 안 되는데요? Nestia는 NestJS로 작성된 백엔드 코드를 읽고 TypeScript compiler를 이용해서 프론트에서 사용 가능한 SDK를 만든다. 그래서 지금 사용하면 곤란한 TypeScript keyword들이 있는데, 하나는 type

kscodebase.tistory.com

 

응답 형태를 변환해주는 유틸리티성 함수

export function createResponseForm<T>(data: T, requestToResponse?: `${number}ms`): ResponseForm<T> {
  return {
    result: true,
    code: 1000,
    requestToResponse,
    data,
  } as const;
}

응답의 경우는 바로 리턴하기 좋게 createResponseForm 이라는 함수를 만들어주었다.

requestToResponse는 추후 요청으로부터 응답까지 몇 초가 걸렸는지를 체크하기 위해 만든 값이다.

이 글에서 얘기하려는 것과는 무관하니, 그냥 그런 게 있다고만 생각하고 넘어가도 좋다.

이 함수는 응답의 형태를 아래와 같이 바꿔준다.

 

// nest.js code

@Get('user/:id')
async getHello(@Param('id', ParseIntPipe) userId: number) {
	return createResponseForm('hello'); // { result: true, code; 1000, data: 'hello' }
}
// if you use nestia library, you can make API more faster.

@TypedRoute.Get('user/:id')
async getHello(@TypedParam('id', 'number') userId: number) {
	return createResponseForm('hello'); // { result: true, code; 1000, data: 'hello' }
}

위에는 일반적인 NestJS 코드이며, 아래는 Nestia 라이브러리를 사용한 형태이다.

이제 에러가 날 경우에는 에러 객체를 리턴하는 식으로 API를 만들면 프론트에서 에러조차 알 수 있게 된다.

 

TryCatch<T,E> 타입 : 성공하거나 실패하거나

export type KeyOfError = keyof typeof ERROR;
export type ValueOfError = (typeof ERROR)[KeyOfError];
export type TryCatch<T, E extends ValueOfError> = ResponseForm<T> | E;

여기까지 따라왔다면 이제부터 모든 API의 타입은 TryCatch<T, E>로 정의될 수 있다.

성공할 경우에는 데이터를 ResponseForm<T> 형태로 바꿔주며, 그렇지 않은 경우에는 Error를 반환한다.

만약 에러가 나올 수 없는 형태의 API 라면, 그냥 응답만 준다고 명시한다.

Error의 형태는 응답의 형태와 동일하되 각각의 키에 대한 값이 조금씩 다를 뿐이다.

 

에러가 없는 함수라면?

export type Try<T> = ResponseForm<T>;

그냥 에러가 없다고 명시해주기만 하면 된다.

물론 여기서 말하는 에러는, 엄밀히 말해 예외처리된 에러를 말하는 것이다.

따라서 서버에서도 예상 못한 에러 ( ex. timeout ) 가 발생할 수 있다는 것은 항상 염두해두자.

 

함수의 리턴 타입을 TryCatch<T, E>로 바꾸기 예시

/**
 * @summary 230207 - 유저가 다른 유저를 팔로우하는 API
 *
 * @tag users
 * @param userId
 * @param followeeId
 * @returns 성공 시 true를 반환한다.
 * @throws 4008 이미 좋아요를 누른 유저입니다!
 * @throws 4009 팔로우할 유저를 찾지 못했습니다.
 * @throws 4017 설마 자기 자신을 팔로우하려고 했어요?!
 */
@TypedRoute.Post(':id/follow')
async follow(
  @UserId() userId: number,
  @TypedParam('id', 'number') followeeId: number,
): Promise<
  TryCatch<
    true,
    | typeof ERROR.ALREADY_FOLLOW_USER // { result: false, code: 4008, message: '이미 좋아요를 누른 유저입니다!' }
    | typeof ERROR.CANNOT_FIND_ONE_DESIGNER_TO_FOLLOW // { result: false, code: 4008, message: '팔로우할 유저를 찾지 못했습니다.' }
    | typeof ERROR.CANNOT_FOLLOW_MYSELF // { result: false, code: 4008, message: '설마 자기 자신을 팔로우하려고 했어요?!' }
  >
> {
  if (userId === followeeId) {
    return ERROR.CANNOT_FOLLOW_MYSELF;
  }
  const response = await this.usersService.follow(userId, followeeId);
  return response === true ? createResponseForm(response) : response;
}

 

타입 추론이 올바르게 동작하는지 확인하기

it('유저가 다른 사람을 팔로우하는 경우에 대한 테스트', async () => {
  // NOTE : 팔로우할 다른 유저를 생성
  const userData = typia.random<CreateUserDto>();
  const follower = await AuthApis.sign_up.signUp({ host }, userData);

  const response = await UserApis.follow.follow( // user/:id/follow 경로의 follow Api는
    {
      host,
      headers: {
        Authorization: token, // 이 토큰에 해당하는 유저가
      },
    },
    follower.data.id, // 이 유저를 팔로우하는 경우
  );

  if (response.code === 1000) { // 응답의 코드가 1000이면 데이터는 true를 던질 것이며,
    response.data;
  } else if (response.code === 4008) { // 그 외라면 에러를 던진다.
    response.data; // 에러인 경우 응답의 data에는 에러 메시지를 호출하기 전에 미리 알 수 있다.
  } else {
    // 아직 명시되지 않았지만 에러가 추가될 수 있는 경우 else문으로 처리해준다.
    response.data;
  }

  expect(response.data).toBe(true);
});

Nestia를 활용한 e2e 테스트 방식은 무척이나 간단하다.

위 코드는 해당 e2e 테스트에서 응답 타입을 보여주기 위해 if문으로 된 분기처리를 추가한 코드이다.

각각의 if문 스코프 내에서 code에 따라 다른 data가 추론되는 것을 확인할 수 있다.

동일한 코드에 대해 아래 이미지를 통해 확인해보자.

 

응답에 마우스를 올리니 리턴 타입이 유니온으로 모두 표기되는 것을 볼 수 있다.
code 값으로 분기처리된 내부에서 에러가 점점 세분화되는 것을 볼 수 있다.

data의 타입은 string이 아니라 확정된 에러 메시지다.

따라서 프론트 개발자는 swagger 문서를 열 게 아니라, 마우스를 코드 위에 올리면 결과를 확인할 수 있다.

프론트 개발자도 당연 Node.js ( 그리고 아마도 타입스크립트? ) 개발자일 텐데 왜 문서로 얘기해야 하는가?

Node.js 생태계의 개발자들이라면 코드로 얘기하는 것이 훨씬 더 간편하고 직관적이며 또한 효율적이다.

 

SDK 배포하기

tsconfig.api.json

{
  "compilerOptions": {
    "target": "ES5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    "module": "CommonJS" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    "lib": ["DOM", "ES2015"] /* Specify library files to be included in the compilation. */,
    "declaration": true /* Generates corresponding '.d.ts' file. */,
    "outDir": "./packages/api/lib" /* Redirect output structure to the directory. */,
    "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */,
    "strict": true /* Enable all strict type-checking options. */,
    "noUnusedLocals": true /* Report errors on unused locals. */,
    "noUnusedParameters": true /* Report errors on unused parameters. */,
    "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
    "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
    "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
    "stripInternal": true,
    "skipLibCheck": true /* Skip type checking of declaration files. */,
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  },
  "include": ["src/api"]
}

SDK만 따로 build할 목적으로 tsconfig.api.json을 생성한다.

 

SDK 빌드하기

npx nestia sdk
tsc -p tsconfig.api.json

SDK만 따로 빌드한다.

기존에 src/api에 있던 SDK와, 그 SDK 함수들이 참조하고 있던 모든 타입들을 js로 바꿔 패키지로 만든다.

배포하기

// 폴더 구조에서 대부분을 생략하고, 여기서 설명이 필요한 대상만 작성하였다.

ㄴpackages/api/lib
    ㄴpackage.json
ㄴsrc
ㄴtsconfig.json
ㄴtsconfig.api.json
ㄴpackage.json

빌드까지 완료되었다면 폴더 구조에는 packages/api/lib이 생긴다.

이 안에다가 package.json을 하나 더 만든다.

이는 package를 배포하기 위한 설정들을 추가하기 위한 json 파일로, 바깥의 package.json과 다른 용도다.

 

// packages/api/lib/package.json

{
  "name": "server-apis",
  "version": "0.0.0",
  "description": "API for PROJECT",
  "main": "index.js",
  "typings": "index.d.ts",
  "repository": {
    "type": "git"
  },
  "author": "kakasoo", // 자기 이름으로 바꾸도록 하자
  "license": "MIT",
  "bugs": {
    "url": ""
  },
  "homepage": "#readme",
  "dependencies": {
    "@nestia/fetcher": "^1.0.1",
    "typia": "^3.6.8"
  }
}

현재까지는 이런 형태로 되어 있는데, 이 글을 읽는 사람들은 꼭 버전을 확인하고 진행하자.

물론 이 버전들로도 동작하는 데에는 아무런 문제가 없을 것이다.

 

npm publish

npm whoiam, npm login, npm pack, npm publish 명령어들은 배포를 하기 위해 알아두면 좋은 명령어다.

이 명령어들을 이용해서 npm에 로그인하고, 패키지를 만들어보고, 그리고 배포하는 과정을 거친다.

npm whoiam, login은 git으로 치자면 git config에 해당하며,

npm pack은 git status,

npm publish는 git push에 해당한다고 생각하면 된다.

npm에는 add, commit 과정이 없는 대신 .npmignore 파일을 이용해 배포에서 제외할 대상을 지정할 수 있다.

 

프론트에서의 활용법

import * as Apis from "picktogram-server-apis/api/functional";

Apis.api.v1.auth
    .login(
        {
            host: "<http://127.0.0.1:3000>", // localhost라고 쓰면 에러가 나니 http로 시작하는 주소를 넣자.
        },
        {
            email: "user@example.com",
            password: "string",
        }
    )
    .then((res) => console.log(res));

테스트 코드와 동일하게 동작한다.

앞으로 프론트 개발자와의 커뮤니케이션, 버전 관리 문제에서 해방되었다!

 

나도 그렇게 생각해!

 

반영되어야 할 사항

리턴 타입이 너무 긴 경우

현재는 너무 긴 타입에 대해서는 타입이 { … } 형태의 말줄임표로 변환되는 문제가 있다.

이 문제는 아래 PR에 나온대로 코드를 수정하면 해결할 수 있다.

다만 아직 Samchon님이 merge를 승인해주지 않았으니 로컬에 nestia를 install하여 임시방편으로 쓰면 된다.

 

 

fix : Too long return type fails to generate SDK #282 by kakasoo · Pull Request #284 · samchon/nestia

ts.TypeFormatFlags.NoTruncation apply I don't know how to test it work correctly, so I need your help. I have previously said in issue #282 that "the problem is that too long types are not properl...

github.com

 

현재는 해결되었다.

 

 

Fix #282 and Complement #284 - full name typing by samchon · Pull Request #286 · samchon/nestia

Solution by @kakasoo

github.com

 

 

예제코드

아쉽게도 여기에 사용된 예제 코드는 모두 private로, 내 사이드 프로젝트 코드이다.

내게는 유감이지만 사이드 프로젝트가 망한 경우 ( 또는 가망이 없다고 판단된 경우 ) 코드를 공개할 예정.

 

23.04.09

망한 건 아닌데 코드를 공개하기로 했다!

계속 코드는 추가되고 있기 때문에 이 글을 읽을 시점에는 예제 코드가 무척 달라져 있을 수 있다.

사실 이걸 작성하는 시점에도.

 

https://github.com/picktogram/server/

 

GitHub - picktogram/server

Contribute to picktogram/server development by creating an account on GitHub.

github.com

 

반응형