Next.js로 프로젝트를 진행하던 중 Secure쿠키를 백엔드와 주고받기 위해서 ssl인증서를 서버에 적용시켜야 하는 경우가 발생했다.
nginx를 사용하면 손쉽게 ssl인증키를 적용시킬 수 있겠지만 해당 프로젝트는 그 당시까지 실 배포단계까지 가야하는 사이즈가 아니여서 고민하던 찰나.. Next자체 서버를 사용하는게 아닌 커스텀서버를 만들어서 ssl인증서를 추가해줄수 있는 방식이 있어 해당 방법으로 가기로 결정하였다
해당 방법을 이용하면 발급한 ssl인증서를 적용시킬 수 있는데 이번 포스팅에서는 Next자체 서버가 아닌 Next.js의 커스텀 서버를 만들어서 입맛대로 사용하는 과정을 살펴보자
커스텀 서버, 말 그대로 Next의 자체 서버를 사용하는게 아닌 개발자가 입맛에 맞게 바꾼 서버를 커스텀 서버라고 한다.
커스텀 서버가 없는 Next프로젝트의 경우 npm run start, npm run dev를 실행하면 Next의 자체서버가 실행된다.
커스텀 서버를 구축하기 전에 주의할 점은 커스텀 서버를 "왜?" 사용하는지에 대해서 많이 고민해보고 구축하길 추천한다.
커스텀 서버는 특정 상황에 대해서만 사용한다.
Next의 자체서버는 서버리스 함수와 최적화와 같은 중요한 성능 최적화 역할도 해주고 있는데 커스텀 서버를 쓴다는건 앞서 말한 자체 서버의 장점을 배제한다는 단점이 있다.

커스텀 서버로 사용할 파일(server.ts)을 하나 만들어준다.
커스텀 서버를 적용할 프로젝트에서는 TypeScript로 진행하고 있어서 ".ts"확장자를 가진 파일을 만들었다.
이름과 위치는 상관없다.
그 후, 해당 파일에다가 커스텀 서버를 작성하면 된다.
커스텀 서버에 대해서 어느정도 지식이 있는 사람이라면 베이스에서 시작해도 충분하겠지만 커스텀 서버에 대해서 베이스가 없다면 Next의 레퍼런스에서 제공하는 기본틀을 커스텀 해도 된다.
Next 레퍼런스에서 제공하는 커스텀 서버 기본 틀(JavaScript)
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const hostname = 'localhost'
const port = 3000
// when using middleware `hostname` and `port` must be provided below
const app = next({ dev, hostname, port })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer(async (req, res) => {
try {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
if (pathname === '/a') {
await app.render(req, res, '/a', query)
} else if (pathname === '/b') {
await app.render(req, res, '/b', query)
} else {
await handle(req, res, parsedUrl)
}
} catch (err) {
console.error('Error occurred handling', req.url, err)
res.statusCode = 500
res.end('internal server error')
}
})
.once('error', (err) => {
console.error(err)
process.exit(1)
})
.listen(port, () => {
console.log(`> Ready on http://${hostname}:${port}`)
})
})
Configuring: Custom Server ==> 커스텀 서버에 대한 설명과 기본틀이 있는 Next레퍼런스 링크
새롭게 만든 커스텀 서버(TypeScript)
import http from 'http' //node.js의 내장 http모듈 -> HTTP서버 및 클라이언트 생성하는 기능
import { parse } from 'url' //Node.js의 "parse"함수 -> 주어진 URL을 구문 분석하여 URL의 구성 요소를 추출
import next from 'next' //Next.js의 dev옵션
import fs from 'fs' //외부파일인 ssl인증서를 읽기위한 "fs"모듈
import https from 'https' //node.js의 내장 https모듈 -> HTTPS서버 및 클라이언트 생성하는 기능
const dev: boolean = process.env.NODE_ENV !== 'production' //개발 환경 여부를 나타내는 boolean, 즉 production모드인지 dev모드인지를 구분하기 위해 사용
const app = next({ dev }) //"dev"옵션을 통해 개발모드인지 여부를 설정해서 "app"변수에 초기화
const handle = app.getRequestHandler() // Next의 애플리케이션 요청 핸들러를 가져온다. 모든 HTTP요청을 처리한다.
const PORT: number = 8000 //서버가 실행될 포트를 설정, 백엔드에서 3000번대를 쓰고 있어서 8000으로 지정
app.prepare().then(() => { //"app.prepare().then"은 애플리케이션이 준비되면 실행될 초기화 함수를 정의할 수 있다. 여기서는 HTTP와 HTTPS 2가지 경우로 나눴다.
http //HTTP서버를 생성하고 listen한다. "createServer()"메서드를 사용하여 HTTP요청을 처리하고 요청 핸들러인 "handle"
.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {
const parsedUrl = parse(req.url!, true)
handle(req, res, parsedUrl)
})
.listen(PORT, (err?: Error) => {
if (err) throw err
console.log(`> Ready on http://localhost:${PORT}`)
})
const options = {
key: fs.readFileSync('./localhost-key.pem'), // 발급한 ssl인증키를 "readFileSync"로 읽기
cert: fs.readFileSync('./localhost.pem'),
}
https
.createServer(options, function (req: http.IncomingMessage, res: http.ServerResponse) {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url!, true)
handle(req, res, parsedUrl)
})
.listen(PORT + 1, (err?: Error) => { //HTTPS같은 경우 8001번 포트로 연결
if (err) throw err //에러 발생시 서버를 돌리지 않고 에러를 던지기
console.log(`> Ready on https://localhost:${PORT + 1}`) //서버가 성공적으로 실행될 시 포트가 준비되었다는 문구 출력
})
})
커스텀 서버를 적용할 프로젝트에서는 전체를 싹 바꿀정도로 커스텀하는게 아닌 ssl인증서를 추가하는 조건만 있어서 약간의 수정만 했다.
또한 예시 코드는 JavaScript코드여서 해당 코드를 TypeScript로 수정했다.
여기까지 진행했다면 70%정도 왔다..
고생해서 만든 커스텀 서버는 기존에 서버를 돌려주던 "package.json"의 "scripts"에 의해서 돌아가지 않는다.
즉, "scripts"를 수정해야 한다.
기존의 "scripts"

수정한 "scripts"

커스텀 서버를 TypeScript로 작성했기 때문에 "ts-node"로 커스텀 서버를 실행해야 한다.
또한 "scripts"에 있는 "server.ts"의 경로를 정확하게 지정해야 한다.
기존의 개발 모드로 돌렸던 "dev"를 ts-node server.ts로 재정의 해야 한다.
또한 프로덕션 모드로 돌렸던 "start" 또한 NODE_ENV=production ts-node server.ts로 재정의 해야 한다.
이는 코드 실행 시 Node.js에게 현재 환경이 프로덕션임을 알려주는 것이다.
여기까지 커스텀 서버를 실행시키기 위한 준비작업은 끝났다. 이제 서버를 돌려보자.
위와같이 커스텀 서버가 정상적으로 실행되는걸 확인할 수 있다.
제한된 환경에서 ssl인증키를 적용시키기 위해서 커스텀서버를 만들어 보았다.
커스텀 서버를 구축하면서 느낀점은 생각보다 건드려야 할 부분이 꽤 있고 자잘한 에러가 많이 난다는 거다.
위 과정은 간단해보이지만 저 과정중에 큰 시행착오는 아니지만 자잘한 시행착오가 꽤 있었다.
nginx를 이용하면 쉽게 해결할 문제지만 때로는 이렇게 돌아가는 방법도 한번쯤은 경험해보아서 꽤 색다른 경험이었다.
물론 다음에 Next로 프로젝트를 진행한다고 하면 ssl인증은 nginx를 사용할거 같지만..
https://nextjs.org/docs/pages/building-your-application/configuring/custom-server
https://medium.com/%EB%8F%84%EA%B9%A8%EB%B9%84-%EC%9D%B4%EC%95%BC%EA%B8%B0/next-js%EB%9E%80-next-js-%EC%84%9C%EB%B2%84-%EC%BB%A4%EC%8A%A4%ED%84%B0%EB%A7%88%EC%9D%B4%EC%A7%95-b814392f9b80
감사합니다^^ 도움 받아서 넥스트 서버 돌려보네요!