반응형

Node.js 개발에서 TypeScript를 도입할 때 가장 먼저 마주치는 고민 중 하나는 바로 이것입니다.

TypeScript를 전역으로 설치할까? 로컬로 설치할까?

이 글에서는 두 방식의 차이점, 장단점, 실제 추천 시나리오까지 한번에 정리해드립니다.


✅ 전역 설치란?

전역 설치는 시스템 전체에 TypeScript를 설치하여 어느 프로젝트 디렉토리에서도 tsc 명령어를 사용할 수 있게 하는 방식입니다. 다음과 같은 명령어로 설치합니다:

npm install -g typescript

설치 후에는 터미널 어디에서나 다음 명령이 작동합니다:

tsc --version
tsc yourfile.ts

전역 설치의 장점

  • tsc 명령을 터미널 어디서든 바로 쓸 수 있다
  • CLI 툴 사용 시 빠르게 실행 가능
  • 전역 설정에 익숙한 개발자에게 편리

전역 설치의 단점

  • 버전 충돌 가능성: 프로젝트마다 TypeScript 버전이 다를 경우 문제 발생
  • 팀 협업에서 위험: 다른 개발자의 시스템에 같은 버전이 설치되어 있다고 보장할 수 없음
  • CI/CD 파이프라인에서 재현 불가

✅ 로컬 설치란?

로컬 설치는 해당 프로젝트 폴더 안에 TypeScript를 설치하는 방식입니다. 보통 개발 의존성으로 추가합니다:

npm install --save-dev typescript

설치 후에는 전역 명령 대신 npx를 사용해 실행합니다:

npx tsc
npx tsc --init

로컬 설치의 장점

  • 프로젝트별로 TypeScript 버전을 고정할 수 있어 안정성 확보
  • 모든 팀원이 동일한 환경에서 작업 가능
  • CI/CD 환경에서도 100% 재현 가능
  • package.json만 보면 어떤 버전이 쓰였는지 알 수 있다

로컬 설치의 단점

  • tsc를 직접 실행하려면 npx 또는 경로를 지정해야 한다
  • 여러 프로젝트를 오가며 전역 설치처럼 쓰고 싶으면 약간 번거로움

🔍 어떤 상황에 어떤 설치를 써야 할까?

  • 혼자 실습하거나 빠르게 테스트하는 경우
    전역 설치도 무방합니다.
  • 팀 프로젝트, 버전 관리, 협업, CI/CD 구성이 필요한 경우
    반드시 로컬 설치가 권장됩니다.

요즘 대부분의 실제 프로젝트는 typescript를 로컬로 설치하고 npx tsc 또는 tsc npm script로 실행합니다.


🧙 마무리

전역 설치는 빠르고 편리하지만, 협업과 재현성을 위해서는 로컬 설치가 훨씬 안전하고 추천되는 방식입니다.

최종적으로는 다음과 같은 전략을 추천합니다:

  • CLI 툴 실습이나 글로벌 테스트용 → 전역 설치
  • 실제 서비스, 팀 프로젝트, 오픈소스, CI/CD → 로컬 설치 (with --save-dev)

🎯 참고 명령어 요약

# 전역 설치
npm install -g typescript

# 로컬 설치
npm install --save-dev typescript

# 로컬 실행
npx tsc

 

728x90
반응형
반응형

Step1: 프로젝트 초기 설정

패키지 관리자는 yarn을 사용였습니다. 

먼저 프로젝트에 대한 새 디렉토리를 만들고 해당 디렉토리로 이동 후  Yarn 프로젝트를 초기화합니다.

mkdir my-project
cd my-project
yarn init -y

 

Step2: 프로젝트 초기 설정

Node.js, TypeScript, Express, TypeORM 및 PostgreSQL에 대한 패키지를 설치합니다.

yarn add express typeorm reflect-metadata pg
yarn add -D typescript ts-node @types/node @types/express

 

1. 'yarn add express typeorm reflect-metadata pg'

이 명령어는 express, typeorm, reflect-metadata, pg 패키지를 설치합니다. 이 패키지들은 프로젝트의 런타임 의존성으로 설치됩니다.

  • express: Node.js를 위한 빠르고 간단한 웹 프레임워크입니다. HTTP 서버를 쉽게 구축할 수 있게 해줍니다.
  • typeorm: TypeScript와 JavaScript(ES7, ES6, ES5)를 위한 ORM(Object Relational Mapper)입니다. 데이터베이스와 상호작용을 쉽게 해줍니다.
  • reflect-metadata: 메타데이터를 사용하여 객체지향 프로그래밍을 지원하는 데 필요한 라이브러리입니다. TypeORM에서 데코레이터 기능을 사용할 때 필요합니다.
  • pg: PostgreSQL 데이터베이스와 상호작용하기 위한 PostgreSQL 클라이언트입니다.

2. 'yarn add -D typescript ts-node @types/node @types/express'

이 명령어는 typescript, ts-node, @types/node, @types/express 패키지를 개발 의존성으로 설치합니다. 즉, 이 패키지들은 개발 중에만 필요하며, 프로덕션 빌드에는 포함되지 않습니다.

  • typescript: TypeScript 컴파일러입니다. TypeScript 코드를 JavaScript로 변환해줍니다.
  • ts-node: TypeScript 실행 환경입니다. TypeScript 코드를 실행할 수 있게 해줍니다.
  • @types/node: Node.js의 타입 정의 파일입니다. TypeScript에서 Node.js 내장 모듈을 사용할 때 타입 정보를 제공합니다.
  • @types/express: Express의 타입 정의 파일입니다. TypeScript에서 Express를 사용할 때 타입 정보를 제공합니다.

Step3: TypeScript 설정

프로젝트 루트경로에 tsconfig.json 파일을 생성하고 TypeScript 설정을 구성합니다.

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules"]
}

 

Step4: TypeORM 구성 설정

프로젝트 루트경로에 src/data-source.ts 파일을 생성하고 설정을 구성합니다.

import "reflect-metadata";
import { DataSource } from "typeorm";
import { User } from "./entity/User";

export const AppDataSource = new DataSource({
  type: "postgres",
  host: "localhost",
  port: 5432,
  username: "your-username",
  password: "your-password",
  database: "your-database",
  synchronize: true,
  logging: false,
  entities: [User],
  migrations: [],
  subscribers: [],
});

 

'your-username', 'your-password', 'your-database'는 PostgreSQL 설치할때 설정 값으로 바꿉니다

 

Step:5 엔티티 생성

User 및 GenderStatistics 엔티티를 생성합니다.

User 엔티티

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id!: number;

  @Column()
  firstName!: string;

  @Column()
  lastName!: string;

  @Column()
  age!: number;
}

 

GenderStatistics 엔티티

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()
export class GenderStatistics {
  @PrimaryGeneratedColumn()
  num!: number;

  @Column({ nullable: true })
  poll_id!: string;

  @Column({ nullable: true })
  trait!: string;

  @Column({ nullable: true })
  gender!: string;

  @Column({ nullable: true })
  gender_cnt!: number;

  @Column("float", { nullable: true })
  rate!: number;
}

 

rate 필드는 소수점 값으로 저장할 수 있도록 설정하였습니다.

 

Step6: 리포지토리 생성

위의서 만든 각 엔티티에 해당하는 리포지토리를 생성하여 데이터베이스 작업을 처리합니다.

 

userRepository.ts

import { AppDataSource } from "../data-source";
import { User } from "../entity/User";

const userRepository = AppDataSource.getRepository(User);

export const createUser = async (userData: Partial<User>): Promise<User> => {
  const user = userRepository.create(userData);
  return await userRepository.save(user);
};

export const getUserById = async (id: number): Promise<User | null> => {
  return await userRepository.findOneBy({ id });
};

 

genderStatisticsRepository.ts

import { AppDataSource } from "../data-source";
import { GenderStatistics } from "../entity/GenderStatistics";

const genderStatisticsRepository = AppDataSource.getRepository(GenderStatistics);

export const createGenderStatistics = async (data: Partial<GenderStatistics>): Promise<GenderStatistics> => {
  const genderStat = genderStatisticsRepository.create(data);
  return await genderStatisticsRepository.save(genderStat);
};

export const getGenderStatisticsByNum = async (num: number): Promise<GenderStatistics | null> => {
  return await genderStatisticsRepository.findOneBy({ num });
};

 

Step7: 컨트롤러 생성

컨트롤러를 생성하여 HTTP 요청을 처리합니다.

 

userController.ts

import { Request, Response } from "express";
import { createUser, getUserById } from "../repositories/userRepository";

export const createUserHandler = async (req: Request, res: Response) => {
  try {
    const user = await createUser(req.body);
    res.status(201).json(user);
  } catch (error) {
    if (error instanceof Error) {
      res.status(500).json({ message: error.message });
    } else {
      res.status(500).json({ message: "An unknown error occurred" });
    }
  }
};

export const getUserByIdHandler = async (req: Request, res: Response) => {
  try {
    const user = await getUserById(parseInt(req.params.id));
    if (user) {
      res.json(user);
    } else {
      res.status(404).json({ message: "User not found" });
    }
  } catch (error) {
    if (error instanceof Error) {
      res.status(500).json({ message: error.message });
    } else {
      res.status(500).json({ message: "An unknown error occurred" });
    }
  }
};

 

genderStatisticsController.ts

import { Request, Response } from "express";
import { createGenderStatistics, getGenderStatisticsByNum } from "../repositories/genderStatisticsRepository";

export const createGenderStatisticsHandler = async (req: Request, res: Response) => {
  try {
    const genderStat = await createGenderStatistics(req.body);
    res.status(201).json(genderStat);
  } catch (error) {
    if (error instanceof Error) {
      res.status(500).json({ message: error.message });
    } else {
      res.status(500).json({ message: "An unknown error occurred" });
    }
  }
};

export const getGenderStatisticsByNumHandler = async (req: Request, res: Response) => {
  try {
    const genderStat = await getGenderStatisticsByNum(parseInt(req.params.num));
    if (genderStat) {
      res.json(genderStat);
    } else {
      res.status(404).json({ message: "Gender statistics not found" });
    }
  } catch (error) {
    if (error instanceof Error) {
      res.status(500).json({ message: error.message });
    } else {
      res.status(500).json({ message: "An unknown error occurred" });
    }
  }
};

 

Step8: 라우트 설정

userRoutes.ts

import { Router } from "express";
import { createUserHandler, getUserByIdHandler } from "../controllers/userController";

const router = Router();

router.post("/users", createUserHandler);
router.get("/users/:id", getUserByIdHandler);

export default router;

 

genderStatisticsRoutes.ts

import { Router } from "express";
import { createGenderStatisticsHandler, getGenderStatisticsByNumHandler } from "../controllers/genderStatisticsController";

const router = Router();

router.post("/gender-statistics", createGenderStatisticsHandler);
router.get("/gender-statistics/:num", getGenderStatisticsByNumHandler);

export default router;

 

Step9: Express 서버 설정

index.ts

import "reflect-metadata";
import express from "express";
import { AppDataSource } from "./data-source";
import userRoutes from "./routes/userRoutes";
import genderStatisticsRoutes from "./routes/genderStatisticsRoutes";

AppDataSource.initialize().then(() => {
  const app = express();

  app.use(express.json());

  app.use(userRoutes);
  app.use(genderStatisticsRoutes);

  const port = process.env.PORT || 3000;
  app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
  });
}).catch(error => console.log(error));

 

모두 작성 되었다면 yarn dev로 개발 서버를 실행합니다.

 

자바스크립트 계열은 자료를 찾다보면 작성자마다 구조와 형식이 달라 늘 어렵네요.

728x90
반응형

+ Recent posts