이 글은 프로그래밍 경험이 없는 분들을 대상으로 자바스크립트 웹 앱 제작법을 알려드리는 ‘웹 개발 배우기’ 시리즈의 일부인데요.
지금까지는 우리의 모든 자바스크립트 코드를 .html 파일이든 .js 파일이든 단 하나의 파일에 담아왔는데요.
이번 시간에는 코드를 여러 파일로 나누는 방법과, 우리가 작성한 코드가 제대로 동작하는지 자동으로 검증하는 방법에 대해 알아볼 겁니다.
화살표 함수, 이렇게도 쓸 수 있어요 #
지금까지 우리는 항상 이런 형태의 화살표 함수를 사용해왔는데요.
(x) => { /* body */ }
이 문법은 괄호 안의 0개 이상의 파라미터와 중괄호 안의 코드 블록(body)으로 구성되어 있습니다.
화살표 함수를 다르게 쓰는 두 가지 방법을 살펴볼게요.
괄호 생략하기 #
파라미터가 딱 하나일 때는 괄호를 생략할 수 있거든요.
x => { /* body */ }
하지만 파라미터가 두 개 이상이거나 하나도 없다면, 괄호는 필수입니다.
표현식 본문(Expression bodies) #
함수의 본문으로 표현식(expression) 자체를 사용할 수도 있는데요.
아래 세 가지 화살표 함수는 모두 똑같이 동작합니다.
(x) => { return expr }
(x) => expr
x => expr
단순히 어떤 값을 반환하는 것 이상의 작업을 해야 한다면, 반드시 중괄호를 사용한 코드 블록 형태를 사용해야 합니다.
어떤 문법을 써야 할까요? #
그럼 어떤 문법을 사용하는 게 좋을지 궁금하실 텐데요.
화살표 함수가 단지 표현식 하나만 반환한다면 ‘표현식 본문’이 훨씬 간결하고 편리합니다.
개인적으로는 짧은 표현식 본문이 한 줄에 쏙 들어갈 때만 파라미터의 괄호를 생략하는 편이거든요.
예를 들면 x => x + 1 처럼 말이죠.
코드를 분리하는 기술, 모듈(Modules) #
먼저 simple-module/library.js라는 모듈 파일부터 살펴보겠습니다.
export const add = (x, y) => x + y;
‘모듈’은 그냥 자바스크립트 파일의 또 다른 이름이라고 생각하면 편한데요.
원래 모듈 안에서 만든 것들은 그 모듈 안에서만 쓸 수 있지만, 변수 선언 앞에 ’export’를 붙이면 이야기가 달라집니다.
이제 import를 사용해서 다른 모듈에서 add 함수를 가져올 수 있거든요.
simple-module/main.js 파일을 한번 보시죠.
import { add } from './library.js';
console.log('One plus one is ' + add(1, 1));
첫 줄에 있는 import 구문을 자세히 뜯어볼까요?
from './library.js'는 어떤 파일에서 코드를 가져올지 알려주는 부분이고, import { add }는 그 파일에서 정확히 무엇을 가져올지 지정하는 부분입니다.
‘노드제이에스(Node.js)‘로 main.js 파일을 직접 실행해보면 의도한 대로 잘 동작하는 걸 확인할 수 있을 겁니다.
cd learning-web-dev-code/projects/
node simple-module/main.js
라이브러리가 뭔가요? #
‘라이브러리(library)‘는 다른 모듈에게 특정 기능을 제공하는 하나 이상의 모듈 묶음을 말하는데요.
simple-module/library.js 파일이 바로 그런 라이브러리인 셈입니다.
네임스페이스 가져오기(Namespace imports) #
모듈을 가져오는 또 다른 방법으로, 가져온 모듈을 위한 ‘네임스페이스(namespace) 객체’를 만드는 방식이 있는데요.
이 방식을 사용하면 main.js 코드는 아래와 같이 바뀝니다.
import * as lib from './library.js';
console.log('One plus one is ' + lib.add(1, 1));
lib라는 새로운 변수가 library.js 모듈에서 export된 모든 것을 프로퍼티로 가지는 하나의 객체를 가리키게 되는 겁니다.
노드제이에스의 내장 모듈 #
노드제이에스에는 node:로 시작하는 여러 내장 모듈이 있는데요.
예를 들어 node:fs는 파일 시스템에 접근할 수 있게 해주는 모듈입니다.
함수의 동작을 자동으로 테스트하기 #
library.js 모듈에 있는 add 함수를 다시 한번 볼까요?
const add = (x, y) => x + y;
이 함수가 제대로 동작하는지 확인하려면, 보통 자바스크립트 콘솔에서 이렇게 테스트해볼 텐데요.
하지만 코드를 수정하고 기능이 복잡해질수록, 이 과정을 계속해서 반복해야 한다는 단점이 있습니다.
‘자동화된 테스트’는 바로 “이런 반복적인 확인 작업을 컴퓨터가 대신해주면 어떨까?” 하는 생각에서 출발했거든요.
assert로 값이 같은지 확인하기
#
체크를 자동화하려면, 먼저 두 값이 같은지 확인할 방법이 필요한데요.
노드제이에스에는 이를 위한 ’node:assert’라는 내장 모듈이 있습니다.
이 모듈은 ‘반드시 참이어야 하는 것’을 단언(assert)하는 함수들을 제공하거든요.
import * as assert from 'node:assert/strict';
const toUpper = str => str.toUpperCase();
assert.equal(toUpper('yes'), 'YES');
만약 단언이 사실이 아니면 프로그램은 그 자리에서 멈추고 에러를 보고합니다.
assert.equal()은 원시 값과 객체를 다룰 때 === 연산자와 똑같은 문제를 가지고 있는데요.
객체의 내용물을 비교해주는 assert.deepEqual() 함수도 있습니다.
노드제이에스의 내장 테스트 러너 #
사실 assert만으로도 add() 함수를 테스트할 수는 있는데요.
하지만 검증할 항목이 많아지면 코드에 좀 더 체계적인 구조가 필요해지거든요.
바로 이럴 때 노드제이에스의 내장 ‘테스트 러너(test runner)‘가 아주 유용합니다.
import { test } from 'node:test';
import * as assert from 'node:assert/strict';
import { add } from './library.js';
test('add() must work for numbers and strings', () => {
assert.equal(add(1, 2), 3);
assert.equal(add('yes', 'no'), 'yesno');
});
테스트는 test() 함수를 통해 등록하는 하나의 함수인데요.
테스트를 등록할 때는 어떤 테스트인지 설명하는 이름을 붙여줍니다.
파일 이름 끝에 붙은 _test는 테스트 코드가 담긴 파일임을 나타내는 일반적인 약속입니다.
자, 그럼 테스트 러너를 직접 실행해 볼까요?
cd learning-web-dev-code/projects/
node --test simple-module/library_test.js
셸 명령어에 --test 옵션을 추가하면 노드제이에스가 테스트 러너 모드로 전환됩니다.
다음 프로젝트를 위한 준비 운동 #
이 섹션에서는 다음 프로젝트에 필요한 몇 가지 개념들을 미리 배워볼 텐데요.
조금 복잡한 내용도 있으니, 모든 걸 완벽하게 이해하지 못해도 괜찮습니다.
텍스트의 단위들 #
자바스크립트에서 텍스트를 다루는 중요한 단위 세 가지가 있는데요.
첫 번째는 ‘문자(character)’, 두 번째는 유니코드 표준의 문자인 ‘코드 포인트(code point)’, 세 번째는 화면에 표시되는 하나의 ‘글자’를 나타내는 ‘그래핌 클러스터(grapheme cluster)‘입니다.
문자와 숫자 사이의 변환 #
엄밀히 말해, 코드 포인트는 사실 숫자인데요.
string.codePointAt()을 사용하면 특정 문자의 코드 포인트를 얻을 수 있고, String.fromCodePoint()는 코드 포인트 숫자를 다시 문자열로 바꿔줍니다.
while 반복문
#
while 반복문은 if문과 비슷하지만, 조건이 참인 동안 ‘계속해서’ 실행된다는 큰 차이점이 있습니다.
숫자 순환시키기 #
배열 같은 값을 반복적으로 순회하다가 끝에 도달하면 다시 처음부터 시작하고 싶을 때가 있는데요.
자바스크립트의 ‘나머지 연산자(%)‘를 사용하면 이 과정을 아주 간단하게 구현할 수 있습니다.
const inc = (len, index) => {
return (index + 1) % len;
};
프로젝트 encode-decode-text/
#
‘ROT13’은 텍스트를 암호화하고 복호화하는 아주 간단한 방법인데요.
알파벳을 13자리씩 밀어서 문자를 바꾸는 방식이라 ‘Rotate13’이라고도 불립니다.
알파벳은 총 26글자이기 때문에, 암호화하는 것과 복호화하는 것이 완전히 똑같습니다.
ROT13은 어디에 쓸까요? #
ROT13은 보안이 중요한 정보를 암호화하는 데는 적합하지 않은데요.
하지만 온라인에서 스포일러나 짓궂은 농담 같은 내용을 바로 보이지 않게 살짝 가리는 용도로는 종종 사용됩니다.
encode-decode-text/rot13.js
#
rot13() 함수를 구현하기 위해, 먼저 문자를 13자리 밀어주는 rot13Char() 함수를 만듭니다.
문자의 코드 포인트 값을 이용해 상대적인 위치를 계산하고, 13을 더한 후 26으로 나눈 나머지를 구해 알파벳 범위를 벗어나지 않도록 순환시킵니다.
이 함수를 이용해서 rot13() 함수를 구현하면, 대소문자를 구분해서 알파벳만 변환하는 코드가 완성됩니다.
encode-decode-text/rot13_test.js
#
rot13Char()와 rot13() 함수를 위한 테스트 코드는 다음과 같은데요.
import { test } from 'node:test';
import * as assert from 'node:assert/strict';
import { rot13, rot13Char } from './rot13.js';
test('rot13Char()', () => { /* ... */ });
test('rot13(): once', () => { /* ... */ });
test('rot13(): twice', () => { /* ... */ });
마지막 테스트에서는 문자열을 두 번 암호화하면 원래대로 돌아오는지 확인하는데요.
테스트를 작성할 때는 빈 문자열처럼 예상치 못한 값도 잘 처리하는지 확인하는 것이 아주 중요합니다.
rot13()을 위한 웹 UI
#
rot13.js는 브라우저 코드와 노드제이에스 코드 양쪽에서 모두 사용하는 라이브러리인데요.
encode-decode-text.html 파일은 웹 브라우저에서 rot13() 함수를 사용할 수 있는 UI를 제공합니다.
자바스크립트 코드에서 import { rot13 } from './rot13.js'; 처럼 모듈을 가져오는 것을 볼 수 있는데요.
이것이 브라우저 코드에서 함수를 import하는 첫 번째 사례입니다.
아쉽게도 웹 브라우저는 file:// URL을 가진 웹 페이지에서 모듈을 import하는 것을 허용하지 않거든요.
따라서 우리는 웹 서버를 실행해야만 합니다.
cd learning-web-dev-code/projects/
npx http-server
이제 http://127.0.0.1:8080/encode-decode-text/encode-decode-text.html 주소로 접속하면 웹 앱을 사용할 수 있습니다.