

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 입력 (Window 10 ver 이상)
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
x64 머신용 최신 WSL2 Linux 커널 업데이트 패키지wsl --set-default-version 2 
sudo apt-get updatesudo apt-get install redis-serversudo service redis-server start 입력redis-cli 입력ping 입력 → pong 답변 확인
npm init -y로 json 파일 생성npm i typescript 로 타입스크립트 설치tsconfig.json 파일 생성 후 아래와 같이 설정{
"compilerOptions": {
"target": "ES2016",
"module": "CommonJS",
"outDir":"./build",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true
},
"include": ["app/**/*.ts"] // app 폴더 안에 있는 어떤 파일이던 .ts 로 끝나면 적용해라
}
npx tsc 명령어 실행 시 build 폴더가 생기고 그 아래에 index.js 파일이 생김
✨ 현재 설정 정리: npm run build로 타입스크립트 파일을 자바스크립트로 바꿔주고, npm run start로 생성된 자바스크립트 파일을 실행해줌.
npm i -D nodemon concurrently{
"name": "express",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node build/index.js",
"build": "npx tsc",
"dev": "concurrently \"npx tsc --watch\" \"nodemon build/index.js\" "
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"typescript": "^5.6.3"
},
"devDependencies": {
"concurrently": "^9.1.0",
"nodemon": "^3.1.7"
}
}
tsc는 app 폴더 안의 파일들이 바뀌고 있는지 관찰하면서 바뀔 때마다 ts → js로 변경해줌"concurrently \"npx tsc --watch\"nodemon은 변환된 것을 확인했을 때 바로 restart\"nodemon build/index.js\"Ctrl + C로 켰던 nodemon 서버를 꺼줄 수 있음npm i -D @types/node @types/expressindex.ts 파일에 express import해서 서버파일 작성import express from "express";
const app = express();
app.use(express.json()); // json 형식으로 데이터를 받을 것이므로 json 파싱
app.get("/", (req, res) => {
res.status(200).send("Hello from Express");
});
const PORT = 4000;
app.listen(PORT, () => {
console.log(`App listening at port ${PORT}...`);
});
GET http://localhost:4000 으로 요청 보내서 Hello from Express 출력 확인


Ctrl + J로 터미널 열고, 그림과 같이 powershell에서 ubuntu로 변경하기
redis-server로 열기
sudo apt-get updatesudo apt-get install redis-serverSET, GET 사용: SET으로는 SET 키 값 형태로 입력해서 키의 이름과 값을 설정할 수 있고, GET으로는 GET 키 이름을 입력하면 키 값이 반환되게 됨
LPUSH, LRANGE 사용
Ctrl + C
✨ 그런데 서버를 켜두기 위해 항상 터미널을 켜두는 것은 번거로우므로 자동으로 늘 켜두자
redis-server --daemonize yesredis-clishutdownnpm i redis 설치// index.ts
import express from "express";
import * as redis from "redis";
const PORT = 4000;
const LIST_KEY = 'messages'
const createApp = async() => {
const app = express();
const client = redis.createClient({ url: "redis://localhost:6379" });
await client.connect();
app.use(express.json());
app.get("/", (req, res) => {
res.status(200).send("hello from express");
});
// end point
app.post("/messages", async(request, response) => {
const { message } = request.body;
await client.lPush(LIST_KEY, message);
response.status(200).send("Message added to list");
});
app.get("/messages", async(request, response) => {
const messages = await client.lRange(LIST_KEY, 0, -1);
response.status(200).send(messages);
});
return app;
};
createApp().then((app) => {
app.listen(PORT, () => {
console.log(`App listening at port ${PORT}...`);
});
});
http://localhost:4000/messagesredis-cli로 작성한 메시지들은 보이지 않음.. 그치만 일단 GET, POST requst는 작동함
{
"message": "hello redis"
}
이후 GET 요청도 POST 요청내용을 올바르게 잘 반영해서 나옴

npm i -D jest @types/jest ts-jest"test": "jest --watchAll"
jest.config.js 파일 추가module.exports = {
preset: "ts-jest/presets/js-with-ts",
testEnvironment: "node",
};
app 폴더에 index.test.ts 테스트 파일 생성describe("jest", () => {
it("should work", () => {
expect(200).toBe(200);
});
});
저장 시 자동으로 테스트 결과가 나옴

테스트 파일 수정 시 서버 rebuild를 방지하기 위해서 ts.config.json 파일에 "exclude": ["**.test.ts"] 추가
동기 처리는 순차적으로 작업을 수행합니다. 어떤 작업이 완료되기 전까지 다음 작업이 실행되지 않으며, 각 작업이 직렬로 실행됩니다. 예를 들어, 1번 작업이 끝나야 2번 작업이 시작될 수 있고, 2번 작업이 끝나야 3번 작업이 시작될 수 있습니다. 이런 구조는 코드가 실행되는 순서가 보장되기 때문에 이해하기 쉽지만, 특정 작업이 오래 걸리면 전체 프로그램이 멈춰버리는 단점이 있습니다.
function task1() {
console.log("Task 1 시작");
for (let i = 0; i < 1e9; i++); // 긴 작업
console.log("Task 1 완료");
}
function task2() {
console.log("Task 2 시작");
}
task1();
task2();
Task 1 시작
Task 1 완료 (시간 소요)
Task 2 시작
비동기 처리는 특정 작업이 끝날 때까지 기다리지 않고 다음 작업을 실행합니다. 예를 들어, 네트워크 요청이나 파일 읽기처럼 시간이 오래 걸릴 수 있는 작업을 비동기로 처리하면, 결과를 기다리는 동안 다른 작업을 할 수 있습니다. JavaScript에서는 비동기 작업이 완료되면 콜백 함수를 통해 결과를 처리하거나, Promise 또는 async/await 문법을 사용하여 비동기를 구현합니다.
function task1() {
console.log("Task 1 시작");
setTimeout(() => {
console.log("Task 1 완료");
}, 3000); // 3초 후 실행
}
function task2() {
console.log("Task 2 시작");
}
task1();
task2();
Task 1 시작
Task 2 시작
Task 1 완료 (3초 후)
async와 await는 JavaScript에서 비동기 작업을 쉽게 다루기 위해 제공하는 문법입니다. async 키워드를 함수 앞에 붙이면 해당 함수는 항상 Promise를 반환합니다. 함수 내부에서 await 키워드를 사용하면 Promise가 완료될 때까지 해당 줄에서 기다리게 됩니다. 이는 마치 동기 코드처럼 보이게 만들지만 실제로는 비동기적으로 처리되어 코드의 가독성을 높입니다.
async function fetchData() {
console.log("데이터 가져오는 중...");
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await response.json();
console.log("데이터 가져오기 완료:", data);
}
fetchData();
console.log("다른 작업 수행 중");
데이터 가져오는 중...
다른 작업 수행 중
데이터 가져오기 완료: { ... }
await을 붙여서 오래 걸리는 작업이 실행중이더라도 그 다음코드가 함께 실행될 수 있도록 함await를 사용하는 경우에 함수 앞에 async를 붙여줘야 함npm i -D supertest @types/supertestapp 폴더 > index.test.ts 파일에 본격적으로 테스트 코드 작성import request from "supertest";
import createApp from ".";
// let app: Express.Application;
import { App } from "supertest/types";
let app: App;
beforeAll(async() => {
app = await createApp()
})
describe("POST /messages", () => {
it("responds with a success message", async() => {
const response = await request(app)
.post("/messages")
.send({ message: "testing with redis" });
expect(response.statusCode).toBe(200);
expect(response.text).toBe("Message added to list");
})
})
describe("GET /messages", () => {
it("responds with all messages", async () => {
const response = await request(app).get("/messages")
expect(response.statusCode).toBe(200);
// expect(response.body).toEqual([]);
});
});
expect(response.body).toEqual([]); 를 넣어서 실행해보면 초기의 response의 body는 빈 배열이어야 하는데, 테스트를 실행할 때마다 post에서 보낸 메시지가 계속 출력되고 있음을 알 수 있음..

// index.test.ts
import request from "supertest";
import createApp, { RedisClient } from "./app";
import * as redis from "redis";
import { LIST_KEY } from "./app";
// let app: Express.Application;
import { App } from "supertest/types";
let app: App;
let client: RedisClient;
beforeAll(async() => {
client = redis.createClient({ url: "redis://localhost:6379" });
await client.connect();
app = createApp(client)
})
beforeEach(async() => {
await client.flushDb();
})
afterAll(async() => {
await client.flushDb();
await client.quit()
})
describe("POST /messages", () => {
it("responds with a success message", async() => {
const response = await request(app)
.post("/messages")
.send({ message: "testing with redis" });
expect(response.statusCode).toBe(200);
expect(response.text).toBe("Message added to list");
})
})
describe("GET /messages", () => {
it("responds with all messages", async () => {
// 여기서 데이터를 넣어주고, 마지막에 toEqual로 검사해보자
await client.lPush(LIST_KEY, ["msg1, msg2"]);
const response = await request(app).get("/messages")
expect(response.statusCode).toBe(200);
// expect(response.body).toEqual([]);
expect(response.body).toEqual(["msg1, msg2"]);
});
});
// index.ts
import * as redis from "redis";
import createApp from "./app";
const PORT = 4000;
const startServer = async() => {
const client = redis.createClient({ url: "redis://localhost:6379" });
await client.connect();
const app = createApp(client)
app.listen(PORT, () => {
console.log(`App listening at port ${PORT}...`);
});
}
startServer();
// app.ts
import express from 'express';
import { RedisClientType } from 'redis';
export const LIST_KEY = 'messages'
export type RedisClient = RedisClientType<any, any, any>;
const createApp = (client: RedisClient) => {
const app = express();
// const client = redis.createClient({ url: "redis://localhost:6379" });
// await client.connect();
app.use(express.json());
app.get("/", (request, response) => {
response.status(200).send("hello from express");
});
// end point
app.post("/messages", async(request, response) => {
const { message } = request.body;
await client.lPush(LIST_KEY, message);
response.status(200).send("Message added to list");
});
app.get("/messages", async(request, response) => {
const messages = await client.lRange(LIST_KEY, 0, -1);
response.status(200).send(messages);
});
return app;
};
export default createApp;
PORT=4000
REDIS_URL=redis://localhost:6379
const REDIS_URL = "redis://default:test_env@localhost:6380";redis-server --daemonize yes --port 6380 --requirepass test_env 명령으로 다시 켜기redis-cli -p 6380으로 접속해야 함!auth test_env로 이전에 설정한 비밀번호를 입력하고 나면 OK가 뜸. 그 이후부터는 모든 명령어 입력이 다시 가능해짐. npm i로 항상 package.json에 있는 애들을 다 설치하는 용으로 사용해왔는데, 만약 개발용으로 devdependency 버전으로 설치한 모듈이 있는 경우에는 npm install -D로 싹 다 설치해줘야 함!redis-server --daemonize yes로 서버를 하나 시작하고, 그 뒤에는 redis-server --daemonize yes --port 6380 --requirepass test_env
여기까지 완료하고 나면 local machine에서 node.js app이 redis 서버 위에서 돌아가고 있는데, github에 업로드한 후 VM server (AWS)에서 pull로 가져옴으로서 이제는 VM서버 위에서 돌려볼 예정이다.
git init: .git 파일 생성git status : 현재 git에 올라가있는 파일들을 조회하고, 만약 node_modules 등 불필요한 것들이 올라가있는 경우 .gitignore라는 이름의 파일을 만들고 아래와 같이 불필요한 파일/폴더명을 적어준 후 다시 git status 명령어로 잘 제외되었는지 확인하기.
git add .git commit -m "first commit"git remote add origin <깃허브레포HTTP주소>: push 전에 어디에 push해야할지, 레포지토리에 대한 정보를 줘야 함.git push origin main: 여기서 보안상 push할 권한이 없다고 나올 거임. 왜? 이건 private 으로 생성된 repository이니까. 아래에서 SSH로 보안처리를 해보자. ssh-keygen -t rsa -b 4096 -C "galaxybook pro"ls ~/.ssh로 생성된 키 목록을 조회할 수 있으며 결과적으로, id_rsa, id_rsa.pub만 보면 되고, id_rsa.pub가 public key임. cat ~/.ssh/id_rsa.pub으로 public key 조회하기
git push --set-upstream origin master 로 push 성공
메모리가 1GB, CPU가 2개는 되었으면 해서 얘를 고름 (첫 3달은 무료라고 써있으니 그 전에 해제하면 과금되지는 않을 것임)



원래는 SSH 인증을 해서 로컬에서 접근해야 하지만, 우선은 좀 더 간편하게 주황색 네모로 표시된 터미널로 접근해보자.
Node.js 설치 명령어 깃허브 링크 : LTS가 지원되는 Node.js 버전을 찾아서 아래 사진의 명령어 세줄을 돌린 후 node -v로 설치 확인하기 (해당 링크는 Node.js 공식 홈페이지의 '패키지 관리자를 통한 Node.js 설치' 섹션에서 찾을 수 있음)

Redis 설치 명령어 링크 : install redis를 구글링한 후 Linux 선택, 아래의 명령어들 한줄씩 실행하면, 설치 후 바로 실행까지 되고 있음을 확인할 수 있음. curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis


sudo systemctl status redis-server
cat /usr/lib/systemd/system/redis-server.service

After=network.target: 네트워크에 연결되고 나면 실행해라Restart=always: 항상 재시작해라
ssh-keygen -t rsa -b 4096 -C "express lightsail ubuntu-1"
cat ~/.ssh/id_rsa.pub

yes 를 입력하자.. y로는 안됨..
cd express, ls 등의 명령어를 통해 github repo에 업로드되어있는 파일들이 그대로 clone된 것을 확인할 수 있다.
cd express 후 npm install -D : node_modules 설치
npm run build : 여기서 메모리를 1GB 이상으로 선택한 이유가 나옴! 그 이하의 메모리는 간혹 build 실패를 일으킴,, build가 메모리를 많이 소모하기 때문vim .env로 환경변수 파일 생성 후 i를 눌러서 작성 모드로 바꾸고, 앞서 .env 파일에 넣었던 내용들을 입력해주기. 그 후 ESC로 insert 종료하고, :wq로 저장 후 에디터 종료시키기npm run start가 안 먹힘PORT=4000
REDIS_URL=redis://localhost:6379
생성된 파일을 확인하고 싶으면 cat .env로 확인
4. npm start로 실행하기

5. public ip를 복사해서 43.201.5.12:4000 이런 식으로 접근하면 접근이 될 거 같지만 안됨
43.201.5.12:4000 에 접근이 안됐던 이유: Firewall에 노출되지 않았기 때문! 현재 노출된 port 번호는 22, 80번 뿐임. 4000번은 노출되지 않음.
✨ 해결 방법 2가지




.env 파일에 정의해둔 환경변수파일을 수정해서 PORT 번호를 80번으로 바꾸기43.201.5.12 등 <publicip> 만으로 포트번호 기재 없이 접속 가능 (80이 디폴트 포트이므로)cd express, vim .env로 다시 환경변수 파일 접근i, 수정, ESC, :wq 순으로 입력cat .env로 확인npm start로 실행하면 막힘 (80은 디폴트 포트이므로 실행에 권리자권한이 필요하기 때문에!)sudo npm start로 실행해주기



아예 URL이 생김


sudo npm start 명령어로 켜주기



형광펜 부분 클릭해서 Domain 구입하기


이메일 인증 전

이메일 인증 후

Loadbalancer랑 domain 연결하기 위한 certificate 생성하기

domain에 위에서 생성한 backend-ec2.com, www.backend-ec2.com 입력 후 생성

이제 연결!


연결 확인


HTTPS도 추가

원래는 HTTP로 접속 시 아래와 같이 주의 요함으로 나왔는데,

적용 이후에는 HTTP로 접속해도 자동으로 HTTPS로 변경해서 접속시켜줌

✅ 일단 여기까지 해도 대략적으로 배포는 가능!