# 장고에 TypeScript와 SCSS 도입하기

- Author: @baealex
- Published: 2020-06-09
- Updated: 2020-06-09
- Source: http://blex.me/@baealex/django%EC%97%90-typescript%EC%99%80-scss-%EB%8F%84%EC%9E%85%ED%95%98%EA%B8%B0
- Tags: 프론트엔드, 타입스크립트

---

## 결론부터 말하면

스태틱 파일이 포함된 디렉터리를 순회하며 자동으로 `ts`는 `js`로 `scss`는 `css`로 트랜스파일을 해주는 코드를 작성하였다.

<br>

## 왜 도입하게 되었나

블렉스에서 프론트엔드 기술은 순도 100% `JS`와 `CSS`다. `JS`의 경우 최대한 하위호환을 유지하기 위해서 `ES5` 문법으로 코딩하고 있었는데 최근에서야 백틱이 `ES6`에 추가된 문법이라는 것을 알게 되었다. 🤔

```js
var number = 5;
var mixed = `내가 가장 좋아하는 숫자는 ${number}다.`;
```

위 문법에서 사용된 문자가 백틱인데 상당히 충격이었다. 대부분의 경우 위와같이 코드를 작성했기 때문이다. 이후 모든 걸 내려놓고 `ES6` 문법을 사용중이다. 또한 `jQuery`를 남발했던 예전의 나와 `jQuery`를 사용하지 않으려는 지금의 내가 쌓아 온 코드들까지 뒤죽박죽 코드에 진절머리나기 시작했다.

<br>

#### TypeScript

그러다 내 눈엔 타입스크립트가 들어왔다. 자유도가 무척이나 높은 `JavaScript`에 비해 `TypeScript`는 좀 빡빡한 느낌을 줬다. 또한 문법이 다른 언어들과 비슷하게 직교성도 높았고 `ES5`로 컴파일이 가능하다는 점 하나로 도입할 가치가 충분했다.

@gif[https://static.blex.me/images/content/2020/6/9/baealex/21_f4kqXi4pYYrMHDjLjyyl.mp4]

```js
=> TypeScript

// 초기화와 동시에 값을 할당할 경우에는 타입을 안써도 상관없다.
let num: number = 5;
let mention: string = `내가 가장 좋아하는 숫자는 ${num}다.`;

=> ES5

// 컴파일 후 아래와 같이 변환된다.
var num = 5;
var mention = "내가 가장 좋아하는 숫자는" + num + "다.";
```

타입스크립트를 적용한 뒤 내 목표는 단 하나다. `VSCode`에서 단 하나의 빨간줄도 보이지 않는 것. `@types/jQuery`를 추가하지 않는 이상 `jQuery`의 `$`는 빨간줄로 표시된다. `jQuery` 고마웠어... 이제 보내줄게...

<br>

#### SCSS

부수적으로 타입스크립트를 도입하는 동시에 `SCSS`도 도입하고 싶었다. 나는 `SCSS`를 상당히 좋아한다. `@mixin`, `@include`와 같은 문법을 자주 사용하는 건 아니지만 블럭 단위로 스타일시트를 작성한다는 것만으로 정말 훌륭한 라이브러리다.

```css
.blex-write-component input {
	...style...
}
.blex-write-component input:foucs {
	...style...
}
.blex-write-component textarea {
	...style...
}
.blex-write-component .preview {
	...style...
}
```

위와같은 문법을

```sass
.blex-write-component {
	input {
		...style...
		:focus {
			...style...
		}
	}
	textarea {
		...style...
	}
	.preview {
		...style...
	}
}
```

위처럼 작성할 수 있다. 확실히 보기에도 편하고 코딩하기도 좋다. 요즘은 늘어나는 스타일시트에 무언가를 추가하기가 두려워지는데 그럼에도 도입하지 않았던 이유는 당연 장고에서 쿨하게 `SCSS`를 도입하는 방법을 몰랐기 때문이다. 스태틱 파일을 장고로 배포하고 있었다면 여러 꼼수를 부릴 수 있었겠지만 별도의 서버로 운용중이라 미뤄왔다. 하지만 언제까지나 미룰수는 없는 노릇. 트랜스파일링을 하더라도 빠르게 도입하고 싶어졌다.

@gif[https://static.blex.me/images/content/2020/6/9/baealex/21_j0tyuLQizMvNq9xwGINo.mp4]

<br>

#### Transpile with Python

일단 언어의 통일을 위해서 파이썬으로 트랜스파일링을 시도하고자 하였다. 사용하고자 한 라이브러리는 아래 두가지.

- [Dukpy](https://github.com/amol-/dukpy)
- [LibSass for Python](https://github.com/sass/libsass-python)

이들을 활용하여 완벽한 코드를 만들 수 있으리라 생각했는데 `TypeScript`의 변환이 좀 이상했다. 리포에서 예시로 보여준 결과와 달리 보여졌으며 실행이 불가했다.

<br>

#### Transpile with Node

결론은 역시 프론트엔드와 관련된 모든 것은 `Node`가 짱짱이다.

```
npm i typescript
npm i node-sass
```

아주 깔끔하게 작동했다. 필자의 프로젝트에 프론트엔드 파일들은 한 폴더에 몰려있으므로 이 폴더를 순회하며 자동으로 `ts`는 `js`로 `scss`는 `css`로 트랜스파일을 해주는 코드를 작성하였다. `Python`에는 디렉터리를 순회하는 `walk`라는 함수가 있기에 `Node`에도 있지 않을까 생각했지만 헛된 생각이었다. 😅

```js
const fs = require('fs');
const path = require('path');

const ts = require('typescript');
const sass = require('node-sass');

function walk(dir) {
    fs.readdir(dir, (err, filenames) => {
        filenames.forEach((filename) => {
            fs.stat(path.join(dir, filename), (err, stat) => {
                if(stat.isDirectory()) {
                    walk(path.join(dir, filename));
                } else {
                    const filePath = path.join(dir, filename);
                    const ext = filePath.split('.').slice(-1).toString();
                    if(ext === 'ts') {
                        fs.readFile(filePath, 'utf8', (err, source) => {
                            const result = ts.transpileModule(source, {
                                compilerOptions:{
                                    module: ts.ModuleKind.CommonJS
                                }
                            });
                            fs.writeFile(filePath.slice(0, -2) + 'js', result.outputText, (err) => {
                                err && console.log(err);
                            });
                        });
                    } else if(ext === 'scss' || ext === 'sass') {
                        fs.readFile(filePath, 'utf8', (err, source) => {
                            const result = sass.renderSync({
                                data: source,
                                outputStyle: 'compressed'
                            });
                            fs.writeFile(filePath.slice(0, -4) + 'css', result.css, (err) => {
                                err && console.log(err);
                            });
                        });
                    }
                }
            });
        });
    });
}

const targetDrectory = './assets';
walk(targetDrectory);
```

다만 문제라고 해야할지... 오류가 분명 있음에도 컴파일도 매우 잘된다. IDE에서도 확장자가 `.ts` 임에도 `TypeScript`의 문법을 강제하지 않는 느낌도 들었고? 이 부분에 대해선 `TypeScript`에 대해서 더 공부해 봐야 할 듯하다.
