※ 인프런 - 타입스크립트 시작하기(by 이재승) 강의를 기반으로 정리한 내용입니다.
프로젝트 환경 설정
강의 마지막은 간단한 실습 프로젝트! 커맨드라인에서 동작하는 todo앱을 만드는 것이다.
우선 https://github.com/landvibe/inflearn-react-project 에 들어가서 강사님 레포 클론 먼저 하기
그리고 이 프로젝트를 내 로컬에서 실행하기 위해서 npm install을 우선 실행한다.
package.json을 보자.
{
"name": "todo-list",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon --watch '*.ts' --exec 'ts-node' src/index.ts",
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/node": "^14.6.0",
"chalk": "^4.1.0",
"nodemon": "^2.0.4",
"ts-node": "^9.0.0",
"typescript": "^4.0.2"
}
}
dependencies에 보면 설치되어 있는 패키지들이 보인다.
이 프로젝트는 노드 환경에서 실행되기 때문에 노드의 타입 정보가 있는 @types/node를 설치한다.
프로젝트의 start/src/Input.ts를 보면 다음과 같이 작성되어 있다.
import readline from 'readline';
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
export function waitForInput(msg: string) {
return new Promise<string>(res =>
readlineInterface.question(msg, key => {
res(key);
}),
);
}
readline이라는 모듈은 노듈에 있는 모듈이기 때문에 이 패키지에 있는 타입 정보를 활용하게 된다.
chalk는 커맨드라인에서 출력되는 텍스트에 폰트 스타일을 적용하기 위해서 사용된다.
nodemon과 ts-node는 개발 모드에서 편하게 개발하기 위해 사용되는 패키지들이다.
package.json의 start를 보면 "nodemon --watch '*.ts' --exec 'ts-node' src/index.ts" 라고 되어 있다.
즉 노드를 nodemon을 통해 실행하고, 이때 watch모드로 실행한다. watch모드는 파일이 수정될 때마다 뒤에 있는 파일을 실행할 수 있도록 한다. ts파일의 확장자가 수정될 때마다 뒤에 있는 명령어를 실행한다. 그래서 코드를 수정할때마다 노드를 끄고 다시 시작하는 번거로움 없이 바로 확인 가능하다.
ts-node는 ts파일을 자바스크립트 파일로 컴파일하지 않고도 ts파일을 입력으로 받아서 바로 실행할 수 있도록 해준다.
노드 실행은 npm start를 입력하면 된다.
근데 에러 발생.
뭐가 문제인지 찾고 있는데 인프런 커뮤니티에 어느 귀인의 글...
혹시 npm run start시 ts-node 에러나시는 분들은
nodemon --watch *.ts --exec ts-node src/index.ts
이런식으로 따옴표를 제거해야지 제대로 작동합니다.
모르는 분들 계실까봐...
즉 지금 나는 윈도우 운영체제에서 코드를 작성하고 있다보니 명령어가 조금은 달라질 수 있다는 것을 간과하면 안 된다.
start 내 명령어에 작은 따옴표 제거한 결과:
아주 잘된다! 귀인분 감사합니다..!!
암튼 본론으로 돌아와서, 패키지를 다시 보면 스크립트의 build에는 "tsc"가 보이는데, 이는 타입스크립트의 바이너리 파일인 tsc를 실행하겠다는 의미다.
npm run build 명령어로 실행해보자.
빌드를 하면 dist라는 폴더가 생기고, 그 안에는 ts파일들을 컴파일한 js파일들이 생성된다.
start 명령어는 개발할 때 사용하는 것이고, 실제 사용자들에게 전달할 때는 위처럼 js파일로 컴파일한 후, node dist/index.js 명령어로 실행을 해야 한다.
프로젝트 시작
현재 디렉토리의 구성은 다음과 같다(실제 코딩 짜는 부분만 표현)
.
.
├── start
├── dist
│ └── index.js
│ └── Input.js
│ └── util.js
├── node_modules
├── src
│ ├── index.ts
│ ├── Input.ts
│ ├── util.ts
├── package-lock.json
├── package.json
└── tsconfig.json
내가 구현해야 하는 부분은 src 폴더에 있는 파일들이고, 이 프로젝트는 위의 명령어를 설명할 때 말했던 것처럼 index.ts파일을 실행하면서 프로젝트가 시작된다.
먼저 index.ts파일은 function main() 함수를 통해 앱을 실행시킬 것이다. 함수 내 while루프를 통해 입력하는 명령어에 따라 앱이 계속 실행될 수 있도록 하고, Input.ts의 waitForInput() 함수를 사용하여 입력하는 key가 무엇인지 판단하게 할 것이다.
// src/index.ts
import { waitForInput } from "./Input";
async function main() {
while (true) {
// 처음 시작시 화면 클리어
console.clear();
const key = await waitForInput("input command: "); // waitForInput이 프로미스 객체를 반환하기 때문에 async-await 사용
/**
* await는 프로미스 객체가 resolve나 reject가 될때까지 기다려줌
* resolve가 됐을 때 값을 가져옴
*/
}
}
main();
// src/Input.ts
import readline from 'readline';
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
export function waitForInput(msg: string) {
return new Promise<string>(res =>
readlineInterface.question(msg, key => {
res(key);
}),
);
}
다음으로 main()함수에 앱의 상태를 정의하는 객체 state를 만든다.
// src/index.ts
import { waitForInput } from "./Input";
import { Appstate, Priority } from "./type";
import Todo from "./Todo";
async function main() {
// 앱의 상태 정의
const state: Appstate = {
todos: [
new Todo("test1", Priority.High),
new Todo("test2", Priority.Medium),
new Todo("test3", Priority.Low),
],
}
while (true) {
// 처음 시작시 화면 클리어
console.clear();
const key = await waitForInput("input command: "); // waitForInput이 프로미스 객체를 반환하기 때문에 async-await 사용
/**
* await는 프로미스 객체가 resolve나 reject가 될때까지 기다려줌
* resolve가 됐을 때 값을 가져옴
*/
}
}
main();
state의 타입은 인터페이스 Appstate로 만들고, Todo인스턴스의 타입은 enum을 이용하여 Priority로 만들고, todo리스트의 우선순위를 High, Medium, Low로 설정한다.
타입을 설정하는 코드들은 type.ts라는 파일 내에 몰아둔다.
import Todo from "./Todo";
export enum Priority {
High,
Medium,
Low
}
export interface Appstate {
todos: Todo[]; // Todo 클래스의 배열
}
Todo인스턴스는 Todo클래스를 통해 생성한다.
// src/todo.ts
import { Priority } from "./type";
// Todo 클래스
export default class Todo {
static nextId: number = 1; // 객체별로 이 변수를 관리할 필요가 없기 때문에 static으로 정의
constructor(
private title: string,
private priority: Priority,
public id: number = Todo.nextId,
) {
Todo.nextId++;
}
toString() {
return `${this.id}) 제목: ${this.title} (우선순위: ${this.priority})`;
}
}
다음은 커맨드 클래스를 생성하고, 이 클래스를 통해 todo를 커맨드에 출력하는 파일을 만든다.
// src/command.ts
import { waitForInput } from "./Input";
import { Appstate } from "./type";
export abstract class Command { // 추상클래스 Command
constructor(
public key: string, // key: 키보드로 입력한 값
private desc: string
) {}
// 화면에 해당 커맨드가 하는 일이 뭔지 설명해주기 위한 메소드
toString() {
return `${this.key}: ${this.desc}`;
}
// 실행함수. 당장 구체화시키지 않을 것이기 때문에 abstract로 생성
abstract run(state: Appstate): Promise<void>;
}
// 모든 todo 프린트하는 클래스
export class CommandPrintTodos extends Command {
constructor() {
super("p", "모든 할 일 출력하기");
}
async run(state: Appstate): Promise<void> {
for (const todo of state.todos) {
const text = todo.toString(); // 모든 todo 출력
console.log(text);
}
await waitForInput("press any key: ");
}
}
다시 index.ts로 돌아와서, todo리스트를 가져올 배열을 생성하고, 메인 함수에 리스트를 출력하는 코드를 작성한다.
import { Command, CommandPrintTodos } from "./Command";
import { waitForInput } from "./Input";
import Todo from "./Todo";
import { Appstate, Priority } from "./type";
// 커맨드 클래스 통해 todo리스트 가져올 배열 생성
const commands: Command[] = [new CommandPrintTodos()];
async function main() {
// 앱의 상태 정의
const state: Appstate = {
todos: [
new Todo("test1", Priority.High),
new Todo("test2", Priority.Medium),
new Todo("test3", Priority.Low),
],
}
while (true) {
// 처음 시작시 화면 클리어
console.clear();
// todo에 있는 목록 가져오기
for (const command of commands) {
console.log(command.toString());
}
console.log();
const key = await waitForInput("input command: "); // waitForInput이 프로미스 객체를 반환하기 때문에 async-await 사용
/**
* await는 프로미스 객체가 resolve나 reject가 될때까지 기다려줌
* resolve가 됐을 때 값을 가져옴
*/
console.clear();
// 입력한 값이 커맨드에 있다면
const command = commands.find(v => v.key === key);
if (command) {
await command.run(state);
}
}
}
main();
npm start 입력하여 실행
p를 입력하면 state 객체 내에 있는 todo리스트들이 출력된다.
'개발자 도전기 > [STUDY] JS || TS' 카테고리의 다른 글
TypeScript | 강의 메모 | CLI에서 동작하는 todo앱 프로젝트 - Todo 추가, 삭제하기(2) (0) | 2022.04.14 |
---|---|
TypeScript | 강의 메모 | CLI에서 동작하는 todo앱 프로젝트 - Todo 추가, 삭제하기(1) (0) | 2022.04.14 |
TypeScript | 강의 메모 | 타입 추론, 타입가드 (0) | 2022.04.05 |
TypeScript | 강의 메모 | 제네릭(Generic), 맵드 타입(Mapped Type), 조건부 타입(Conditional Type) (0) | 2022.04.01 |
TypeScript | 강의 메모 | 타입 호환성 (0) | 2022.04.01 |
댓글