본문 바로가기
개발/프로젝트-식당 웨이팅 앱 FOOD LINE

[Typescript] TS로 식당 웨이팅 앱을 개발한 이유

by 코딩하는짱구 2024. 1. 16.
반응형

Typescript 기반의 Restful API를 개발한 의사결정의 이유, 그리고 JS 와 TS의 개념을 다시 한번 정리하기 위해 글을 쓴다. 
도대체 Typescript가 왜이렇게 핫한지, 왜 쓰는지 명확하지 않은 분들은 꼭 읽어보기 바란다. 

 

 

Typescript로 식당 웨이팅 앱을 개발한 이유

1. 왜 Typescript 인가?

2. JS를 보완하는 TS의 특징 

3. TS를 사용하며 느낀 점

4. 결론

위의 목차를 클릭하면 해당 글로 자동 이동 합니다.

 

왜 Typescript 인가?

TS는 최근 JS 개발자에게 필수 스펙으로 요구될 만큼 인기가 많은데 
가장 핵심적인 이유는 TS가 동적 언어인 JS를 보완해주기 때문이다. 

JS는 대표적인 동적 언어이다. 
아래에 JS와 TS의 대표적인 차이점인, 데이터 타입 선언의 차이를 들어 쉽게 정리해보겠다. 

// 변수를 선언하고 초기값을 할당합니다.
let myVariable = 10;

// 변수의 데이터 타입을 출력합니다.
console.log(typeof myVariable); // 출력: number

// 같은 변수에 문자열을 할당합니다.
myVariable = "Hello, World!";

// 변수의 데이터 타입을 다시 출력합니다.
console.log(typeof myVariable); // 출력: string

JS는 위 코드 처럼 런타임에 변수의 데이터 타입을 동적으로 결정하므로,
같은 변수가 숫자와 문자열 두 가지 다른 타입의 값을 가질 수 있다. 

즉, JS에서는 변수를 선언할 때 명시적으로 타입을 지정할 수 없다.
이는 개발자가 코드를 작성할 때 데이터 타입을 선언하지 않아도 되는 유연성,
변수에 할당할 데이터 타입의 유연성을 제공한다.

 

상기의 데이터 타입 선언 문제 뿐만 아니라, 동적 언어인 JS의 경우 아래와 같은 단점이 있다. 

1. 런타임에 타입 에러 발견
2. 명시적인 타입 선언이 없으므로 큰 규모의 프로젝트에서는 코드 예측이 어려움
3. JS는 초기에 웹페이지의 상호작용을 위해 설계, 클라이언트 측에서 실행되기에 코드가 사용자에게 노출되는 보안문제 발생

결국 정적 타입인 TS를 사용하는 이유는 위와 같은 JS의 정적 특징을 보완하기 위함이다. 

 

 

 

 

JS를 보완하는 TS의 특징

기존 JS의 단점을 보완 할 수 있는 TS의 특징은 아래와 같다. 

1. TS를 사용하는 가장 큰 이유는 언어의 이름에서도 알 수 있듯 '타입' 이다.
2. 변수의 이름뿐만 아니라 데이터의 자료형 까지 알 수 있기 때문에 코드 작성이 직관적이고 쉽다. 
3. JS의 슈퍼셋 이므로, 기존 JS에 확장하여 TS 적용이 가능하다 -> 호환성
4. JS : ES6부터 클래스/모듈이 도입 되었지만 이전 버전에선 지원이 부족. 
    TS : 클래스와 모듈을 지원하여 객체 지향 프로그래밍 및 모듈화를 더 쉽게 가능
5. JS : 동적이여서 IDE에서 제대로 된 자동 완성과 코드 어시스트 기능을 제공하기 어렵다, 
    TS : 객체안의 필드값을 다 기억할 필요 없이 IDE가 자동으로 리스트 업을 해주므로 생산성이 높아진다. 

 

대체로 위에서 설명했던 얘기지만 마지막 5번은 무슨 말일까?

TS에는 Interface라는 대표적인 기능이 있다.
아래와 같이 Interface를 사용하여 해당 인터페이스를 따르는 객체가
필드와 메서드를 어떻게 구성해야하는지 명시적으로 알려주는 기능이다. 

//TS의 Interface 기능

interface Person {
  name: string;
  age: number;
  sayHello(): void;
}

const person: Person = {
  name: "John",
  age: 30,
  sayHello() {
    console.log("Hello, world!");
  },
};

위의 코드를 보면 Person 이라는 interface를 정의하고, 객체를 생성했다. 

이로써 개발자가 Person이라는 인터페이스를 보면 어떤 객체인지 파악이 쉽고(가독성 up),
유지보수성도 올라가는 것이다.

JS에는 인터페이스와 같은 명시적 타입 정의 기능이 내장되어 있지 않다. 
따라서 아래와 같이 객체를 만들어서 동적으로 필드를 추가할 수는 있겠지만, 
정적인 타입 검사를 할 수 없는 것이다. 

//JS의 동적인 Interface 

// 객체를 만들어서 동적으로 필드를 추가
let person = {};
person.name = "John";
person.age = 30;
person.sayHello = function() {
  console.log("Hello, world!");
};

// 객체의 동적 구조를 활용
console.log(person.name); // John
person.sayHello(); // Hello, world!

 

 

 

 

TS를 사용하며 느낀 점

TS는 컴파일 언어, JS는 인터프리터 언어이다. 
이로 인해 소스코드를 기계어로 번역하는 과정에서 JS에 비해 시간이 오래 걸린다는 단점이 있다고 하는데 .. 
사실 체감을 하진 못했다. 

내가 체감했던 부분은 학습 난이도 차이 인데,
JS는 타입에 제한을 받지 않아 자료형(string, number etc)을 모르더라도 익히는 것이 쉬운 반면에
TS는 타입의 제한을 받기 떄문에 JS보다 난이도가 있다. 

또한 TS는 독자적 언어가 아닌 컴파일 언어이므로, 런타임 환경에서는 JS 코드가 필요하다.
이를 위해 기본적으로 설치해야 하는 모듈들과 옵션 설정을 도와주는 초기 세팅 도구를 사용해야하고,
나의 경우 Nest.js를 사용하였다. 

Nest.js는 프레임워크로써 TS를 JS로 컴파일 하는데 필요한 도구를 제공한다. 
그런데 Nest.js가 기존의 JS/Express보다 난이도가 있다고 느껴졌는데 그 이유는 아래와 같다. 

  • Nest.js는 모듈 기반 아키텍쳐를 채택한다. 
  • 의존성 주입을 통해 코드를 더 모듈화 한다
  • Decorators 같은 메타 프로그래밍 개념
  • Guard, Interceptor, Pipe 등의 개념
  • Class 기반의 컨트롤러

위와 같이 Nest.js는 기본적으로 모듈 기반의 아키텍쳐를 채택하며 의존성 주입을 통해 동작한다. 

 

아래는 실제 프로젝트 코드이다. 

코드를 보면, 
StoresModule 이라는 Nest.js 모듈을 정의했고 'controllers' 및 'providers' 등으로 현재 모듈에서 사용될
컨트롤러, 프로바이더 클래스 들을 정의하고 있다. 

즉, 아래 StoresModule 은 애플리케이션에서 Stores와 관련된 기능들을 모듈화 한 것이다. 

//모듈 기반 아키텍쳐

import { Module } from '@nestjs/common';
import { StoresController } from './stores.controller';
import { StoresService } from './stores.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Stores } from './stores.entity';
import { StoresRepository } from './stores.repository';
import { ReviewsRepository } from '../reviews/reviews.repository';
import { Reviews } from '../reviews/reviews.entity';
import { ElasticsearchModule } from '@nestjs/elasticsearch';
import { CustomCacheModule } from 'src/cache/cache.module';

@Module({
  imports: [
    TypeOrmModule.forFeature([Stores, Reviews]),
    ElasticsearchModule.register({
      node: 'http://52.78.66.6:9200',
      maxRetries: 10,
      requestTimeout: 60000,
      pingTimeout: 60000,
    }),
    CustomCacheModule,
  ],
  controllers: [StoresController],
  providers: [StoresService, StoresRepository, ReviewsRepository],
})
export class StoresModule {}

 

 

아래는 TS의 의존성 주입, 클래스, 데코레이터 등의 특징이 반영된 코드이다. 

//Nest.js의 의존성 주입을 사용하는 서비스 클래스

@Injectable()
export class StoresService {
  constructor(
    @InjectRedis('ec2redis') private readonly redisClient: Redis,
    private storesRepository: StoresRepository,
    private reviewsRepository: ReviewsRepository,

    private readonly elasticsearchService: ElasticsearchService,
  ) {}

위의 코드는 Nest.js에서 의존성 주입을 사용하는 서비스 클래스인 StoresService 이다. 
의존성 주입은 '클래스의 생성자' 에서 외부 의존성을 주입 하는 방식으로 이루어지는데, 
여기서 Injectable() 이라는 '데코레이터'는 이 클래스가 Nest.js에서 관리되는 서비스 임을 나타낸다. 

 

자.. JS/Express 환경에서만 개발해본 사람들은 여기까지 읽었어도 감이 안올 수 있다🤣

결론적으론 JS와는 다른 객체지향적인 기능들 때문에 초기 세팅과 학습 난이도가 조금 있다고 느꼈다는 것이다.

 

 

결론

TS를 익히고 실제로 프로젝트에 사용하면서 모듈 기반의 아키텍쳐에 적응하는 것이 힘들었다. 
하지만 정적언어인 TS를 사용함으로써 객체지향적 프로그래밍을 통해 가독성과 이해도를 높일 수 있었고,
이는 협업을 더욱 효율적이게 만들었다고 생각한다. 

또한, TS가 필수이지만 그렇다고 JS보다 TS가 무조건 좋다!가 아닌,
각 언어의 장단점, 프로젝트 상황에 따라 선택을 해서 사용해야 함을 깨달았다.

다만 TS는
코드가 많아지고 협업하는 개발자가 늘어나며,
정적타입 검사가 중요해지는 대규모 프로젝트에 더 용이할 것이다- 정도로 알고 있으면 되고, 

JS든 TS든 소규모, 대규모 프로젝트를 구축 할 수 있으므로
프로젝트 특성, 팀의 경험, 개발자의 선호도 등으로 선택하면 된다!

 

 

**

아래 블로그에서 Nest.js 기초 세팅, 구성 등을 쉽게 설명하고 있다. 

https://www.wisewiredbooks.com/nestjs/overview/01-firstStep.html

 

첫 걸음 - 쉽게 풀어 쓴 Nest.js

CRUD 기능이 있는 어플리케이션을 같이 만들어보면서 Nest의 핵심 개념에 대해 이해하도록 하겠습니다. Nest는 타입스크립트를 우선적으로 지원하지만 순수 자바스크립트를 사용할 수 있습니다. Ne

www.wisewiredbooks.com

 

 

반응형