[Ubuntu] Nginx 이미지 서버 구축(Nginx로 정적파일 요청하기)

Seung Hyeon ·2024년 3월 2일
1

백엔드

목록 보기
14/19
post-thumbnail

Nginx 이미지 서버 구축이란?

  • 웹 어플리케이션 서버 (WAS)를 거치지 않고 직접 Nginx 웹 서버를 이용하여 이미지 등 정적파일을 제공하는 방법
  • 정적파일은 변경되지 않고 고정된 내용을 제공하는 파일이며, 이를 WAS를 거치치 않고 Nginx을 통해 serving함으로써 더 효율적이고 빠르게 정적 파일을 제공할 수 있다.

AWS S3를 이용한 경험이 있다면 더욱 쉽게 이해할 수 있을 것이다.
S3 버킷에 정적 파일을 저장하면 아래와 같이 파일 url주소가 생성되고 이 url을 클릭하면 이미지가 바로 보여진다(퍼블릭 엑세스를 부여할 경우). 이것과 같은 원리이다.

※ 이미지 출처: https://goddaehee.tistory.com/333


✨ 기존에 사용하던 방식

이미지를 업로드하는 API이미지를 표시하는 API를 통해 정적 파일(이미지)을 제공하는 방식

  1. 클라이언트로부터 이미지를 받아서, 이미지를 'uploads' 폴더에 저장하고 동시에 해당 이미지에 대한 URL을 생성하여 데이터베이스에 저장한 후, 클라이언트에게 이미지 URL을 응답으로 반환한다.
    (이때, 이미지 url을 접속하여 이미지를 바로 확인할 수 있음)
  2. 클라이언트로부터 이미지 파일 이름을 받아서 해당 이미지 파일을 서버에서 찾아 클라이언트로 보내준다.
// 이미지 저장 API
@Put('image/profile')
async updateProfile(
  @UploadedFile() file: Express.Multer.File,
  ): Promise<{ message: string; result: any }> {
    const result = await this.userService.uploadProfile(file.filename);
return { message: '성공적으로 프로필이 업데이트 되었습니다', result };
}

// 이미지 조회 API
@Get('uploads/:filename')
async getImage(@Param('filename') filename: string, @Res() res: Response): Promise<{ message: string }> {
  res.sendFile(filename, { root: 'uploads' });
return { message: '이미지를 성공적으로 가져왔습니다' };
}
📦root
 ┣ 📂src
 ┣ 📂test
 ┣ 📂uploads
 ┃ ┣ 📜1706017805305.png
 ┃ ┣ 📜1706020346012.png

✨ 기존 방식 변경 이유

가장 큰 이유는 "번거로움" 이었다.
AWS S3을 이용하여 이미지를 저장하고 가져왔을 때는 이미지를 조회 API를 따로 구현하지 않아도 생성된 url에 접속해 바로 조회가 가능했지만, 기존 방식은 2개의 api을 호출해야하는 번거로움이 있었다.
따라서 로컬에 저장할 때도 S3와 동일하게 생성된 url에 접속해 바로 이미지를 확인하는 방법이 있는지 찾아보았고 "Nginx을 이용해 이미지 서버를 구축하는 방법"을 찾아냈다.


🌟 Nginx을 이용해 이미지 서버를 구축하는 방법

필자는 Nest.js를 사용하고 있으며 Jenkins을 이용해 도커 컨테이너 서버를 자동 배포하는 프로젝트를 진행 중이며 이 배포는 우분투 서버에서 이루어지고 있다.


먼저 Nginx을 설치한다.

sudo apt-get install nginx 

Nginx가 설치되면 루트 경로로 이동 후, /etc/nginx 디렉토리가 생성되어있는지 확인한다.

cd /
ls /etc/nginx

여기서 정적 파일을 가져오기 위한 웹 설정에 필요한 디렉토리는 sites-availablesites-enabled 이다.
sites-available 디렉터리는 모든 설정을 저장하는 곳, sites-enabled 디렉터리는 sites-available 디렉터리에 작성한 설정을 적용하기 위한 폴더이다.

sites-available 디렉토리에 설정 파일을 만들고 필요할 때마다 이를 sites-enabled 디렉토리에 심볼릭 링크로 추가 또는 제거하여 Nginx가 해당 가상 호스트(홈페이지)를 사용하도록 한다.

sites-available 디렉터리에는 default라는 설정 파일이 기본적으로 존재한다.
default 파일은 Nginx의 기본 설정 파일이며 기본 서버 구성을 변경할 때 사용된다.


Nginx 웹 서버를 설정한다.

  1. sites-availbale > default 파일에 접속
  sudo nano /etc/nginx/sites-available/default
  1. 기존에 적혀있는 설정들은 모두 지우고 아래 내용을 삽입
server {
   # 80번 포트로 서버 오픈
   listen 80;

   # 이미지를 요청할 경로 설정
   location /files/user/ {
        # 해당 경로에서 이미지를 찾는다.(즉 이 dir에 이미지가 저장되어있어야함)
        alias /home/upload/user/;
        # 이미지를 찾지 못했을 경우 404에러 
        try_files $uri $uri/ =404;
   }
}
  • 호스트주소/files/user/image.png으로 접근하면 alias 경로에서 파일을 찾는다
  • 아래와 같이 이미지 태그를 사용해 이미지를 바로 확인할 수 있다.
    <img src="http://[host주소]/files/user/image.png"/>
  1. Nginx 재시작
sudo systemctl restart nginx
  1. 우분투 서버에 /home/upload/user 디렉토리를 생성하여 이미지를 저장할 디렉토리를 생성한다.
mkdir -p /home/upload/user/
  1. 생성한 디렉토리의 권한을 777로 변경한다.
# 해당 디렉토리에 대해 모든 사용자에게 읽기, 쓰기, 실행 권한을 부여
chmod 777 /home/upload

+ 추가 : Nestjs로 이미지 저장하기 - multer module 설정

const UPLOAD_PATH = '/home/upload/user';

@Module({
  imports: [
    MulterModule.register({
      storage: diskStorage({
        destination(req, file, callback) {
          callback(null, UPLOAD_PATH);
        },
        filename(req, file, callback) {
          const uniqueSuffix = `${Date.now()}`;
          const extension = file.originalname.split('.').pop();
          callback(null, `${uniqueSuffix}.${extension}`);
        },
      }),
      fileFilter(req, file, callback) {
        const allowedTypes = ['image/jpeg', 'image/png', 'image/jpg'];
        if (!allowedTypes.includes(file.mimetype)) {
          throw new Error('Invalid file type');
        }
        callback(null, true);
      },
      limits: { fileSize: FILE_SIZE, files: FILES },
    }),
  ],
  exports: [MulterModule],
})

'/home/upload/user' 경로에 이미지를 저장하도록 설정

※ 주의 : /는 루트부터, ./는 현재 경로부터 시작


🌟 Jenkinsfile 수정

기존 Jenkins 파이프라인에 도커 컨테이너에 /home/upload/user 디렉토리를 생성하는 코드와 호스트의 /home/upload 디렉토리를 컨테이너 내부의 /home/upload 디렉토리로 마운트하는 코드를 추가해준다.

sh 'mkdir -p /home/upload/user'
sh 'docker run -d -p 포트:포트 --name 생성할 컨테이너명 -v /home/upload:/home/upload 도커 이미지명'

-v 옵션을 사용하여 호스트 PC의 /home/upload 디렉터리를 컨테이너 내부의 경로와 동기화 시킨다.


💥 에러 발생 : 502 Gateway 오류

원활하게 잘 진행될 줄 알았으나.. /files/user 경로로 이미지를 요청하니 502 Bad Gateway 에러가 나왔다.

해결방법은 아래 명령어를 입력해 방화벽을 해제해야한다.

sudo iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT

iptables는 리눅스 시스템에서 네트워크 패킷을 필터링하고 방화벽 규칙을 설정하는 도구이다.

위 명령어는 방화벽 규칙을 추가하여 포트 80으로 들어오는 TCP 연결을 수락한다.

🌟 모든 작업 끝. 테스트 해보기

이미지가 요청 경로에서 조회가 잘 되는 것을 확인할 수 있다.

디렉토리에도 파일이 잘 들어왔다.


🌟 마무리하며

Nginx로 이미지 서버를 구축해 해당 url로 바로 이미지를 조회할 수 있도록 구현해보았다.
처음에는 AWS S3을 사용하려 했으니 프리티어 제약 때문에 로컬에 저장하기로 했다.
만약 서비스의 규모가 커지게 될 경우 이 방법은 사용이 불가할 것 같다.

한가지 더,

현재 /home/upload/user 디렉토리 안에 이미지 파일이 저장되고 있는데, 파일 자체가 저장될 경우 리소스 비용이 점점 커질 것 같았다. 파일 자체가 아닌 다른 방식으로 저장하는 방법도 한번 찾아봐야겠다.




References

https://wildeveloperetrain.tistory.com/237
https://velog.io/@singsoong/Nginx%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%84%9C%EB%B2%84-%EA%B5%AC%EC%B6%95

profile
안되어도 될 때까지

0개의 댓글