배포는 서버를 다른 컴퓨터에서 켜 두는 일입니다
지금까지 만든 Express 서버는 내 컴퓨터 안에서만 실행되었습니다. 브라우저 주소도 http://localhost:3000이었습니다. 여기서 localhost는 "내 컴퓨터 자신"을 뜻합니다. 그래서 내 컴퓨터에서는 접속할 수 있지만, 다른 사람의 휴대폰이나 노트북에서는 접속할 수 없습니다.
배포는 이 서버를 인터넷에 연결된 다른 컴퓨터에서 켜 두는 일입니다. 식당으로 비유하면, 지금까지는 집 주방에서 혼자 주문과 응답을 연습했습니다. 배포는 작은 가게를 열고, 손님이 바깥에서 주소를 입력해 찾아올 수 있게 만드는 단계입니다.
이번 장에서 만들 것은 새 기능이 아닙니다. 이미 만든 Express 서버를 배포 서비스가 켤 수 있는 형태로 정리합니다. Render나 Railway처럼 GitHub 저장소를 연결하는 서비스도 있고, Fly.io처럼 명령줄 도구를 함께 쓰는 서비스도 있습니다. 화면과 버튼 이름은 달라도 핵심 흐름은 비슷합니다. 코드를 준비하고, 필요한 패키지를 설치하고, 서버를 실행한 뒤, 인터넷 주소를 연결하는 것입니다.
AI 생성 이미지
로컬 서버와 운영 서버는 다르게 실행됩니다
로컬 실행은 내 컴퓨터에서 서버를 켜는 방식입니다.
node app.js성공하면 터미널에 이런 문장이 보이고, 브라우저에서 localhost:3000으로 접속합니다.
Server is running on port 3000운영 서버는 다릅니다. 여기서 운영 서버란 실제 사용자가 접속할 수 있도록 인터넷에 공개된 서버를 말합니다. 운영 서버에서는 우리가 마음대로 3000번 포트로 열겠습니다라고 정하기 어렵습니다.
포트는 건물의 출입문 번호처럼 생각하면 됩니다. 같은 컴퓨터 안에서도 여러 프로그램이 동시에 실행될 수 있으므로, 각 프로그램은 서로 다른 번호의 문을 사용합니다. 로컬에서는 우리가 3000번 문을 골라도 괜찮았습니다. 하지만 많은 호스팅 서비스에서는 서비스가 정해 주는 문 번호를 따라야 합니다.
그래서 배포용 Express 서버는 보통 이렇게 작성합니다.
const PORT = process.env.PORT || 3000;이 한 줄의 뜻은 다음과 같습니다.
process.env.PORT: 운영 서버가 알려 준 포트 번호를 사용합니다.|| 3000: 운영 서버가 포트 번호를 알려 주지 않으면, 연습할 때처럼 3000번을 사용합니다.
즉, 운영 환경에서는 호스팅 서비스의 규칙을 따르고, 내 컴퓨터에서는 익숙한 3000번으로 실행할 수 있게 만드는 코드입니다.
app.js에서 포트 코드를 바꾸기
무엇을 하고 있는지부터 다시 확인하겠습니다. 우리는 배포 서비스가 정해 준 포트로 Express 서버가 실행되도록 바꾸고 있습니다.
먼저 VS Code에서 프로젝트 폴더를 엽니다. 그다음 app.js 파일을 엽니다.
기존 코드 아래쪽에 이런 코드가 있을 수 있습니다.
app.listen(3000, () => {
console.log('Server is running on port 3000');
});이 코드는 로컬 학습용으로는 좋습니다. 하지만 배포 환경에서는 3000번 포트를 직접 고정하면 문제가 생길 수 있습니다. 이제 한 줄씩 바꿔 보겠습니다.
먼저 포트 번호를 변수로 분리합니다.
const PORT = process.env.PORT || 3000;이 변수는 "운영 서버가 포트를 알려 주면 그 값을 쓰고, 없으면 3000을 쓰겠다"는 뜻입니다.
그다음 app.listen()에 숫자 3000 대신 PORT를 넣습니다.
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});최종적으로 아래쪽 코드는 이런 모양이 됩니다.
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});저장한 뒤 터미널에서 서버를 다시 실행합니다.
node app.js성공하면 여전히 다음과 비슷한 문장이 보입니다.
Server is running on port 3000내 컴퓨터에서는 process.env.PORT가 비어 있으므로 3000을 사용한 것입니다. 브라우저에서 http://localhost:3000에 접속했을 때 기존 응답이 보이면 준비가 잘 된 상태입니다.
package.json에 start 명령 추가하기
배포 서비스는 우리의 프로젝트를 받은 뒤 "이 서버를 어떤 명령으로 켜야 하지?"라고 확인합니다. 지금까지 우리는 직접 node app.js를 입력했습니다. 하지만 배포 서비스에서는 보통 npm start처럼 정해 둔 시작 명령을 사용합니다. 서비스 화면에서 시작 명령을 직접 적을 수도 있지만, package.json에 start를 넣어 두면 훨씬 덜 헷갈립니다.
VS Code에서 package.json 파일을 엽니다. 파일 안에 scripts라는 부분이 있을 수 있습니다. 그 안에 start를 추가합니다.
{
"scripts": {
"start": "node app.js"
}
}이미 test 같은 다른 명령이 들어 있다면 지우지 않아도 됩니다. 같은 scripts 안에 start 줄만 추가하면 됩니다. 다만 JSON 파일은 쉼표 위치에 민감합니다. 줄을 추가할 때 쉼표가 빠지거나 마지막 줄에 불필요한 쉼표가 있으면 오류가 날 수 있습니다. 오류가 나도 이상한 일이 아닙니다. 이 파일은 문법 규칙이 조금 엄격한 설정표라고 생각하면 됩니다.
저장한 뒤 터미널에서 직접 확인합니다.
npm start성공하면 내부적으로 node app.js가 실행되고, 터미널에 서버 실행 메시지가 보입니다.
Server is running on port 3000이제 배포 서비스도 같은 방식으로 서버를 켤 수 있습니다.
환경 변수는 배포 서비스 화면에서 넣습니다
지난 장에서 비밀값은 코드에 직접 쓰지 않는다고 배웠습니다. 배포할 때도 같은 원칙이 이어집니다.
예를 들어 .env 파일에 이런 값이 있었다고 해 보겠습니다.
ADMIN_PASSWORD=secret1234
API_KEY=abcdefg이 파일은 GitHub에 올리면 안 됩니다. 보통 .gitignore에 .env를 적어 두어 실수로 올라가지 않게 막습니다. 대신 배포 서비스의 환경 변수 설정 화면에 같은 이름과 값을 등록합니다.
이름:
ADMIN_PASSWORD값: 실제 비밀번호
서버 코드에서는 계속 process.env.ADMIN_PASSWORD로 읽습니다. 코드 입장에서는 로컬에서 .env로 읽든, 운영 서버에서 배포 서비스가 넣어 주든 사용 방식이 같습니다.
만약 CORS를 설정했다면 운영 주소도 확인해야 합니다. 로컬에서는 http://localhost:3000 또는 http://localhost:5173 같은 주소를 허용했을 수 있습니다. 하지만 배포 후에는 실제 서비스 주소가 생깁니다. 예를 들어 https://my-contact-server.example.com 같은 주소입니다. 운영 환경에서는 이 주소를 기준으로 허용 범위를 다시 정해야 합니다.
환경 변수를 새로 추가하거나 값을 바꾼 뒤에는 서버를 다시 배포하거나 재시작해야 적용되는 경우가 많습니다. 값을 입력했는데도 계속 없다고 나온다면, 저장 버튼을 눌렀는지와 재시작이 되었는지도 함께 확인합니다.
GitHub 저장소에 올리는 흐름
많은 호스팅 서비스는 GitHub 저장소와 연결해 배포합니다. GitHub는 코드 보관함입니다. 배포 서비스는 이 보관함에서 코드를 가져와 설치하고, npm start로 서버를 실행합니다.
전체 흐름은 다음과 같습니다.
내 컴퓨터의 프로젝트를 GitHub 저장소에 올립니다.
배포 서비스에서 새 프로젝트를 만듭니다.
GitHub 저장소를 선택합니다.
설치 명령과 시작 명령을 확인합니다.
필요한 환경 변수를 등록합니다.
배포 버튼을 누릅니다.
배포 서비스가 제공한 인터넷 주소로 접속합니다.
처음에는 GitHub와 배포 서비스 화면이 낯설 수 있습니다. 이 단계에서 막히는 것은 자연스러운 일입니다. 중요한 것은 화면 이름을 모두 외우는 것이 아니라, 배포 서비스가 실제로 하는 일을 이해하는 것입니다.
배포 서비스는 보통 다음 일을 순서대로 합니다.
코드 가져오기 → npm install 실행 → npm start 실행 → 인터넷 주소 연결npm install은 package.json을 보고 Express 같은 필요한 패키지를 설치하는 과정입니다. npm start는 우리가 앞에서 추가한 명령입니다. 그래서 package.json의 dependencies와 scripts.start가 중요합니다.
서비스에 따라 GitHub 연결이 아니라 명령줄 도구로 배포하는 경우도 있습니다. 예를 들어 Fly.io는 fly launch, fly deploy 같은 명령을 사용하는 흐름이 자주 나옵니다. 그래도 크게 보면 코드 준비, 설치, 실행, 공개 주소 연결이라는 순서는 같습니다. 초보 단계에서는 특정 서비스의 모든 옵션을 외우기보다 이 큰 흐름을 잡는 것이 더 중요합니다.
배포 서비스에서 확인하는 공통 항목
서비스마다 화면은 다릅니다. 어떤 서비스는 웹 화면에서 대부분을 고르고, 어떤 서비스는 설정 파일이나 터미널 명령을 더 많이 사용합니다. 하지만 확인해야 하는 핵심은 비슷합니다.
첫째, 어떤 프로젝트를 배포할지 확인합니다. GitHub 저장소를 고르는 방식일 수도 있고, 현재 내 컴퓨터의 프로젝트 폴더에서 배포 명령을 실행하는 방식일 수도 있습니다.
둘째, 실행 환경을 확인합니다. Express 서버는 Node.js 앱입니다. 서비스 화면이나 문서에서 Node.js, Web Service, App 같은 표현을 볼 수 있습니다.
셋째, 설치 명령을 확인합니다. 보통은 다음 명령입니다.
npm install넷째, 시작 명령을 확인합니다. 우리는 package.json에 start를 추가했으므로 다음 명령을 사용할 수 있습니다.
npm start다섯째, 환경 변수를 확인합니다. .env 파일에 있던 값 중 운영 서버에서도 필요한 값을 여기에 등록합니다. .env 파일 자체를 업로드하는 것이 아닙니다.
배포가 성공하면 서비스는 공개 URL을 하나 제공합니다. 그 주소를 브라우저에 입력했을 때 Express 서버의 응답이 보이면 인터넷에 서버를 올린 것입니다.
배포 후 접속이 안 될 때 점검 순서
배포 후 바로 성공하지 않아도 괜찮습니다. 운영 서버는 내 컴퓨터보다 확인할 것이 조금 더 많습니다. 대신 순서를 정해 보면 문제를 좁힐 수 있습니다.
먼저 빌드 로그를 봅니다. 로그는 배포 서비스가 코드를 가져오고 설치하고 실행하는 과정을 글로 남긴 기록입니다. 빨간색 오류나 Error, failed, Cannot find module 같은 문장을 찾습니다.
두 번째로 시작 명령을 확인합니다. npm start를 실행했는데 package.json에 start가 없으면 서버를 켤 수 없습니다. start가 있더라도 파일 이름이 다르면 실패합니다. 예를 들어 실제 서버 파일이 server.js인데 node app.js라고 되어 있으면 실행되지 않습니다.
세 번째로 포트 설정을 확인합니다. 코드가 아직도 app.listen(3000)처럼 고정되어 있으면 배포 서비스가 원하는 포트로 열리지 않을 수 있습니다. 다음 형태인지 확인합니다.
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});네 번째로 환경 변수를 확인합니다. 로컬에서는 .env 덕분에 잘 실행되었지만, 운영 서버에는 값이 등록되어 있지 않을 수 있습니다. 데이터베이스 주소, API 키, 관리자 비밀번호처럼 코드에서 process.env로 읽는 값이 있다면 배포 서비스의 환경 변수 화면에 등록되어 있어야 합니다.
다섯 번째로 서버 로그를 확인합니다. 배포는 성공했지만 접속할 때 500 오류가 날 수 있습니다. 이 경우 서버는 켜졌지만 요청을 처리하는 중에 문제가 난 것입니다. 이전에 배운 것처럼 오류 메시지의 핵심 문장, 파일명, 줄 번호를 차례로 읽습니다.
점검 순서를 짧게 정리하면 다음과 같습니다.
빌드 로그 → 시작 명령 → 포트 설정 → 환경 변수 → 서버 로그이 순서대로 보면 어디에서 멈췄는지 찾기 좋습니다.
운영 서버에서는 로그도 조심해서 남깁니다
로컬에서는 console.log(req.body)로 요청 내용을 자주 확인했습니다. 학습 과정에서는 흐름을 이해하는 데 도움이 됩니다. 하지만 운영 서버에서는 로그가 기록으로 남습니다. 이름, 이메일, 전화번호, 비밀번호 같은 값이 로그에 남으면 나중에 문제가 될 수 있습니다.
운영 서버에서는 필요한 정보만 남기는 습관이 좋습니다.
console.log(`${req.method} ${req.path}`);이 정도는 어떤 방식의 요청이 어느 경로로 들어왔는지 확인하는 데 도움이 됩니다. 반면 사용자가 입력한 전체 내용, 비밀번호, API 키, 토큰 같은 값을 그대로 출력하는 것은 피해야 합니다. 주소 뒤에 붙는 긴 query string에도 민감한 값이 들어갈 수 있으니, 꼭 필요한 경우가 아니라면 통째로 남기지 않는 편이 안전합니다.
오류 로그도 마찬가지입니다. 개발자에게 필요한 단서는 남기되, 사용자에게는 너무 자세한 내부 정보를 보여 주지 않는 편이 안전합니다. 사용자는 "서버에서 문제가 발생했습니다" 정도의 메시지를 받고, 자세한 내용은 서버 로그에서 확인하는 방식이 좋습니다.
배포가 되면 서버는 연습 파일을 넘어섭니다
배포 전의 서버는 내 컴퓨터 안에서만 대답했습니다. 배포 후의 서버는 인터넷 주소를 가진 서비스가 됩니다. 다른 사람이 그 주소로 접속할 수 있고, 폼을 제출하거나 JSON API를 호출할 수 있습니다.
그만큼 책임도 함께 생깁니다. 포트는 운영 환경을 따라야 하고, 비밀값은 환경 변수로 관리해야 하며, 로그에는 민감한 정보를 남기지 않아야 합니다. 오류가 나면 빌드 로그와 서버 로그를 차분히 읽어야 합니다.
이번 장의 핵심은 특정 호스팅 서비스의 버튼 위치를 외우는 것이 아닙니다. 배포의 흐름을 이해하는 것입니다.
Express 코드 준비
→ PORT 환경 변수 사용
→ package.json에 start 추가
→ GitHub에 코드 올리기 또는 배포 명령 실행
→ 호스팅 서비스에서 프로젝트 연결
→ 환경 변수 등록
→ 배포 로그 확인
→ 공개 URL 접속이 흐름을 한 번 경험하면 Express 서버는 더 이상 연습용 파일에 머물지 않습니다. 실제 사용자가 접속할 수 있는 작은 서비스가 됩니다. 다음 장에서는 지금까지 배운 라우팅, 폼 처리, JSON 응답, 저장, 오류 처리, 배포 준비를 모아 작은 문의 접수 서버를 완성해 보겠습니다.
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.