Nginx 이미지 서버 구축이란?
AWS S3를 이용한 경험이 있다면 더욱 쉽게 이해할 수 있을 것이다.
S3 버킷에 정적 파일을 저장하면 아래와 같이 파일 url주소가 생성되고 이 url을 클릭하면 이미지가 바로 보여진다(퍼블릭 엑세스를 부여할 경우). 이것과 같은 원리이다.
※ 이미지 출처: https://goddaehee.tistory.com/333
이미지를 업로드하는 API와 이미지를 표시하는 API를 통해 정적 파일(이미지)을 제공하는 방식
// 이미지 저장 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을 이용해 이미지 서버를 구축하는 방법"을 찾아냈다.
필자는 Nest.js를 사용하고 있으며 Jenkins을 이용해 도커 컨테이너 서버를 자동 배포하는 프로젝트를 진행 중이며 이 배포는 우분투 서버에서 이루어지고 있다.
먼저 Nginx을 설치한다.
sudo apt-get install nginx
Nginx가 설치되면 루트 경로로 이동 후, /etc/nginx 디렉토리가 생성되어있는지 확인한다.
cd /
ls /etc/nginx
여기서 정적 파일을 가져오기 위한 웹 설정에 필요한 디렉토리는 sites-available
과 sites-enabled
이다.
sites-available 디렉터리는 모든 설정을 저장하는 곳, sites-enabled 디렉터리는 sites-available 디렉터리에 작성한 설정을 적용하기 위한 폴더이다.
sites-available 디렉토리에 설정 파일을 만들고 필요할 때마다 이를 sites-enabled 디렉토리에 심볼릭 링크로 추가 또는 제거하여 Nginx가 해당 가상 호스트(홈페이지)를 사용하도록 한다.
sites-available 디렉터리에는 default라는 설정 파일이 기본적으로 존재한다.
default 파일은 Nginx의 기본 설정 파일이며 기본 서버 구성을 변경할 때 사용된다.
Nginx 웹 서버를 설정한다.
sudo nano /etc/nginx/sites-available/default
server {
# 80번 포트로 서버 오픈
listen 80;
# 이미지를 요청할 경로 설정
location /files/user/ {
# 해당 경로에서 이미지를 찾는다.(즉 이 dir에 이미지가 저장되어있어야함)
alias /home/upload/user/;
# 이미지를 찾지 못했을 경우 404에러
try_files $uri $uri/ =404;
}
}
<img src="http://[host주소]/files/user/image.png"/>
sudo systemctl restart nginx
mkdir -p /home/upload/user/
# 해당 디렉토리에 대해 모든 사용자에게 읽기, 쓰기, 실행 권한을 부여
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'
경로에 이미지를 저장하도록 설정
※ 주의 : /
는 루트부터, ./
는 현재 경로부터 시작
기존 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 디렉터리를 컨테이너 내부의 경로와 동기화 시킨다.
원활하게 잘 진행될 줄 알았으나.. /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 디렉토리 안에 이미지 파일이 저장되고 있는데, 파일 자체가 저장될 경우 리소스 비용이 점점 커질 것 같았다. 파일 자체가 아닌 다른 방식으로 저장하는 방법도 한번 찾아봐야겠다.
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