Table of contents

혹시 바빠서 해결법만 원하신 분들은 맨 아래 결론을 보시면 됩니다.

배경 상황

최근에 웹 관련 프로젝트들을 이것저것 하게 되면서 만든 프로젝트를 배포하기 위해 서버와 도메인도 샀다. 하지만 매번 프로젝트를 하나씩 추가할 때마다 도메인을 새로 사는 flex를 할 수는 없었다. 그래서 Nginx를 사용해서 여러 프로젝트를 한 도메인에서 배포할 수 있는 환경을 구성하기로 했다. 구체적인 설계는 아래와 같다.

  • 기본 도메인은 http://ialy1595.me/, 서버는 리눅스(우분투)를 사용
  • 기본 페이지에선 프로젝트 목록을 볼 수 있는 playground 프로젝트 배포
  • http://ialy1595.me/project_name/에 해당 프로젝트의 빌드 내용을 배포
    • 이 글의 경우 eng2music 프로젝트 빌드

사실 Nginx는 굉장히 유명한 도구고 위와 같은 상황도 매우 희귀한 케이스는 아닐 것 같아서 검색 조금 하면 해결할 수 있을 줄 알았다. 근데 해결법이 생각보다 명확하게 나와있지 않았다. 특히, 왜 추가하는지 원리를 모르고 일단 따라 쓰라는걸 별로 안 좋아해서 정확히 추가한 설정이 어떤 내용이길래 내 문제를 해결한건지 자세히 찾아보다가 생각보다 삽질을 더 많이 하게 돼서 삽질한 내용 공유해 보고자 한다.

기본 세팅

기본적인 세팅은 이 블로그를 참고했다.

우선 Nginx를 설치하자.

$ sudo apt install nginx

그 다음 기본 설정 파일을 지우고 나만의 설정 파일을 열자.

$ sudo rm /etc/nginx/sites-available/default
$ sudo rm /etc/nginx/sites-enabled/default
$ sudo vim /etc/nginx/sites-available/myapp.conf

이제 여기에 Nginx 설정 내용을 적으면 된다. 우선 기본 페이지를 띄우는 것 부터 해보자. 뒤에 포트번호를 따로 안 적으면 80포트로 접속하게 되니 80포트로 서버를 열어보자. 메인에 띄울 프로젝트에서 빌드를 한 뒤 그 빌드 폴더가 있는 위치를 root로 지정해주면 된다.

server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;
}

이제 저장하고 이 파일을 링크해주자.

$ sudo ln -s /etc/nginx/sites-available/myapp.conf /etc/nginx/sites-enabled/myapp.conf

그 다음 Nginx를 실행하면 된다. 앞으로 설정파일을 수정한 다음에도 아래의 명령어로 실행해주면 된다.

$ sudo systemctl stop nginx
$ sudo systemctl start nginx

메인 도메인으로 접속하면 원하는 페이지가 잘 뜰 것이다.

프로젝트 띄우기

이제 개인 프로젝트를 띄우는 것을 해보자. 우선 다시 설정 파일을 열어보자. Nginx에서 URI에 따른 작업을 하려면 location 블록을 만들면 된다.

server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;

    location /eng2music {
    
    }
}

여기서 location 다음에 있는 /eng2music으로 원하는 URI를 매칭하면 된다. 만약에 더 다양한 작업을 원하면 여기에 정규표현식과 같은 방식도 사용할 수 있는데 자세한건 이 문서를 참고하자.

다시 본론으로 들어와서 이 URI에 내 프로젝트를 연결해주자. 이는 alias에 연결할 프로젝트의 빌드 폴더를 등록해주면 된다.

server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;

    location /eng2music {
        alias /home/ialy/eng2music/build;
    }
}

이제 저장하고 다시 Nginx를 실행해보자.

슬프게도 아마 원하는 대로 잘 안 나올 것이다. 여기서부터 삽질이 시작됐다.

일단 원인부터 알아보자. 크롬의 개발자모드에서 에러창을 보면 404 (Not Found)가 뜬다.

내용을 보면 js랑 css파일을 전부 로드하지 못한다는 것을 알 수 있다. 더 자세한 상황을 분석하기 위해 헤더 내용을 살펴보자.

여기서 문제를 찾을 수 있었다. 이 프로젝트는 http://ialy1595.me/eng2music/에서 열었기 때문에 리소스들도 http://ialy1595.me/eng2music/static/에서 가져와야 한다. 하지면 요청 URL을 보면 http://ialy1595.me/static/에서 가져오려고 하고 있다.

그 이유는 맨 처음 로드되었던 index.html에서 각 파일들을 로드하는 부분을 보면 알 수 있다. 예를 들어 css파일을 로드한는 코드는 다음과 같이 되어 있다.

<link href="/static/css/main.1118e755.chunk.css" rel="stylesheet">

이렇게 상대경로로 지정되어 있는데, index.htmlhttp://ialy1595.me/eng2music/에서 로드되었다고 해도 기본 도메인은 http://ialy1595.me/이 되기 때문에 적용된 경로가 http://ialy1595.me/static/css/main.1118e755.chunk.css가 되는 것이다.

이를 해결하려면 경로를 바꿔주면 된다. 정확히 말하자면 위의 코드를

<link href="/eng2music/static/css/main.1118e755.chunk.css" rel="stylesheet">

로 바꿔주면 될 것이다. 하지만 그렇다고 빌드 폴더의 index.html을 열어서 저 부분을 일일이 수정하기에는 귀찮기도 하고 유지보수성에도 좋지 않다. 다행히 이러한 작업을 위해 Nginx에서 sub_filter라는 옵션을 지원해준다. sub_filter는 옵션 파일에

sub_filter str1 str2

라고 쓰면 여기서 가져온 파일에서 str1이 있는 부분을 전부 str2로 치환하는 전처리 작업을 해준다. 따라서 위의 문제의 경우엔 static으로 시작하는 경로를 eng2music/static으로 시작하도록 바꿔줘야 하니 설정 파일을 아래와 같이 작성하면 된다.

server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;

    location /eng2music {
        alias /home/ialy/eng2music/build;
        sub_filter static/js/ eng2music/static/js/;
        sub_filter static/css/ eng2music/static/css/;
        sub_filter static/media/ eng2music/static/media/;
    }
}

그리고 sub_filter에 관한 몇 가지 설정을 추가해주면 된다. 우선 딱 한 부분만 바꾸는 것이 아니라 저 문자열이 들어있는 모든 부분을 치환해줘야 하니 sub_filter_once off;를 추가해준다. 그리고 그냥 적용하면 html 파일에만 sub_filter 효과가 적용되는데, 우리는 js나 css에 있는 경로도 다 바꿔줘야 한다. 따라서 sub_filter_types *;도 추가해서 다른 확장자 파일에도 적용되도록 해주면 된다.

server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;

    location /eng2music {
        alias /home/ialy/eng2music/build;
        sub_filter static/js/ eng2music/static/js/;
        sub_filter static/css/ eng2music/static/css/;
        sub_filter static/media/ eng2music/static/media/;
        sub_filter_once off;
        sub_filter_types *;
    }
}

여기까지 하면 이제 에러도 안 뜨고 다 잘 되었을 것 같지만 사실 아직 하나가 남아있다. 만약에 프로젝트에 favicon을 적용했다면 알아차릴 수도 있겠지만, 이대로 끝나면 favicon이 프로젝트의 것이 아니라 메인 페이지의 것이 뜨게 된다. 왜냐하면 favicon을 불러오는 부분을 보면

<link rel="icon" href="/favicon.ico"/>

라고 되어있는데, 여기엔 static이라는 문구가 들어가 있지 않아서 위의 설정으로는 링크가 바뀌지 않는다. 따라서 http://ialy1595.me/eng2music/favicon.ico이 아니라 http://ialy1595.me/favicon.ico을 가져오게 된다. 그리고 이는 실제로 메인 페이지의 favicon을 가져오게 되므로 404와 같은 에러는 뜨지 않지만 원하지 않은 결과를 불러오게 된다. 따라서 이 부분도 sub_filter로 수정해준다.

server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;

    location /eng2music {
        alias /home/ialy/eng2music/build;
        sub_filter static/js/ eng2music/static/js/;
        sub_filter static/css/ eng2music/static/css/;
        sub_filter static/media/ eng2music/static/media/;
        sub_filter href="/favicon href="/eng2music/favicon;
        sub_filter_once off;
        sub_filter_types *;
    }
}

이 프로젝트의 경우 여기까지 해주면 모든 리소스가 잘 불러와졌지만, 여러분의 프로젝트에서 불러와지지 않은 것이 더 있다면 그 리소스를 부르는 부분을 찾아서 sub_filter로 올바른 경로로 연결해주면 해결이 될 것이다.

Q: sub_filter static eng2music/static;으로 하면 세 줄이나 안 써도 되지 않나요?

A: static은 좀 흔한 단어기 때문에 코드에서 경로 부분 뿐만 아니라 다른 곳에도 있을 수가 있어서 그러면 그 부분도 전부 치환되어버릴 수 있습니다. 이러한 문제를 방지하기 위해 다른 곳에서는 쓰이지 않을 정도로 자세하게 쓰는게 좋습니다.

결론

  • root를 메인 페이지 빌드 폴더에 연결해준다.
  • location /project_name 블록을 만들어서 그 안에서 alias로 해당 프로젝트 빌드 폴더를 연결해준다.
  • sub_filter로 문자열을 치환해서 리소스 가져오는 부분의 경로를 수정해준다.
    • sub_filter str1 str2;를 추가하면 코드의 str1str2로 바뀐다.
  • sub_filter_once off;, sub_filter_types *;옵션을 더해준다.
server {
    listen 80;

    root /home/ialy/playground/build;
    index index.html;

    location /eng2music {
        alias /home/ialy/eng2music/build;
        sub_filter static/js/ eng2music/static/js/;
        sub_filter static/css/ eng2music/static/css/;
        sub_filter static/media/ eng2music/static/media/;
        sub_filter href="/favicon href="/eng2music/favicon;
        sub_filter_once off;
        sub_filter_types *;
    }
}