Relay는 기존 Roundcube Webmail을 대체하기 위한 Next.js 기반 Webmail 프로젝트입니다.
메일 원본은 기존 Dovecot/IMAP 서버에 그대로 두고, 메일 발송도 기존 Postfix/SMTP 서버를 사용합니다. Relay를 설치한다고 해서 Maildir이나 기존 사용자 메일을 복사·이동·삭제하지 않습니다.
- 반응형 3단 메일 화면, 접을 수 있는 메일 목록과 모바일 레이아웃
- IMAP 서버 전체 검색, 50개 단위 페이지 이동, 필터, 중요 표시, 읽음/읽지 않음
- 답장·전체답장·전달과
In-Reply-To/References스레드 헤더 - HTML 메일 작성기, 굵게·기울임·목록·인용·링크 서식과 본문 이미지 붙여넣기
- 첨부파일 드래그 앤 드롭 업로드, 스트리밍 다운로드, 첨부 포함 전달
- IMAP 임시보관함 자동 저장과 저장된 초안 다시 편집
- 보관, 휴지통 이동과 서버 특수 폴더 자동 탐색
- 보관함·휴지통·스팸함에서 받은편지함 복원
- 휴지통 단일·일괄 영구삭제와 휴지통 비우기 확인 절차
- 메일 다중 선택, 일괄 읽음·중요·보관·삭제·복원
- 이동 작업 실행 취소, 폴더별 상황에 맞는 작업 버튼과 아이콘 툴팁
- 모바일 폴더 메뉴와
/검색 단축키 - IMAP 계정 인증과 실제 폴더/메일 목록 조회 API
- 세션별 IMAP 연결 재사용, IMAP IDLE 실시간 갱신과 브라우저 새 메일 알림
- 같은 제목의 관련 메일 모아보기, 최근 수신자 자동완성과 서버 메일함 용량 표시
- 메일 본문 MIME 파싱과 정화된 HTML·인라인 이미지·HTTPS 외부 이미지 표시
- 신뢰한 메일 서버의 SPF·DKIM·DMARC 인증 결과 표시
- 인증 실패와 From/Reply-To 불일치 경고
- 안전한 이미지·PDF·텍스트 첨부 미리보기, EML 원본 다운로드와 인쇄 화면
J/K이동,R답장,E보관과/검색 단축키- 세션별 SMTP 연결 재사용, 메일 발송 후 IMAP 보낸편지함 저장
- Redis 기반 로그인 세션과 AES-256-GCM 자격증명 암호화
- 애플리케이션·IMAP·SMTP 상태 확인 API와 Docker Compose healthcheck
/는 로그인 세션이 없으면 /login으로 이동합니다. 로그인 후에는 실제 IMAP 폴더, 메일 목록과 본문을 불러옵니다.
로컬에서 실행하려면 다음이 필요합니다.
- Node.js 20.9 이상
- npm
- Redis
- 실제 연결을 시험할 IMAP/SMTP 테스트 계정
- Docker 방식으로 실행할 경우 Docker와 Docker Compose
운영 계정을 바로 사용하지 말고 별도의 테스트 계정으로 먼저 확인하세요.
Windows PowerShell:
Copy-Item .env.example .envLinux/macOS:
cp .env.example .env생성된 .env 파일을 열어 실제 환경에 맞게 값을 수정합니다. .env에는 비밀번호와 secret이 들어가므로 Git에 커밋하면 안 됩니다.
화면에 표시되는 서비스 이름과 문구도 배포 환경마다 바꿀 수 있습니다. 변경 후 애플리케이션을 다시 시작하면 로그인 화면, 상단바, 브라우저 제목과 공유 이미지에 함께 반영됩니다.
WEBMAIL_NAME=Example Mail
WEBMAIL_TAGLINE=Your private webmail가장 일반적인 설정은 다음과 같습니다.
IMAP_HOST=mail.example.com
IMAP_PORT=993
IMAP_SECURE=true
IMAP_POOL_IDLE_SECONDS=120
SMTP_HOST=mail.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_POOL_IDLE_SECONDS=120
# 아이디만 입력했을 때 자동으로 붙일 도메인
MAIL_DOMAIN=example.com
MAIL_AUTH_AUTHSERV_ID=mail.example.com- IMAP 993 포트는 일반적으로 처음부터 TLS를 사용하므로
IMAP_SECURE=true입니다. IMAP_POOL_IDLE_SECONDS동안 사용하지 않은 IMAP 연결은 자동으로 종료되며 다음 요청에서 다시 연결합니다.- SMTP 587 포트는 일반적으로 STARTTLS를 사용하므로
SMTP_SECURE=false입니다. - SMTP 465 포트를 사용한다면 일반적으로
SMTP_SECURE=true입니다. SMTP_POOL_IDLE_SECONDS동안 발송이 없으면 세션별 SMTP 연결을 자동으로 종료합니다.- 로그인 화면은 아이디와 전체 이메일 주소를 모두 허용합니다.
MAIL_DOMAIN=example.com이면gildong과gildong@example.com모두 Dovecot에는gildong으로 인증하고, 화면 표시와 발신 주소에는gildong@example.com을 사용합니다. MAIL_AUTH_AUTHSERV_ID는 Postfix/Rspamd가Authentication-Results에 기록하는 수신 서버 식별자입니다. 발신자 허용 목록이 아니며, 비워두면IMAP_HOST를 사용합니다. 신뢰 ID가 같은 헤더가 여러 개면 SPF·DKIM·DMARC 결과가 가장 완전한 헤더를 화면 판정에 사용합니다.- 사설 인증서를 사용 중이라도 운영 환경에서
IMAP_TLS_REJECT_UNAUTHORIZED=false로 두는 것은 권장하지 않습니다. 신뢰할 수 있는 CA 인증서를 먼저 구성하세요.
Docker 컨테이너에서 호스트 서버의 메일 서비스에 연결할 때 127.0.0.1은 컨테이너 자신을 의미합니다. Windows/macOS Docker Desktop에서는 보통 host.docker.internal을 사용하고, Linux에서는 실제 호스트 IP 또는 별도 Docker 네트워크 구성이 필요합니다.
다음 명령으로 충분히 긴 임의 값을 생성합니다.
node -e "console.log(require('crypto').randomBytes(48).toString('base64url'))"출력된 값을 .env의 SESSION_SECRET에 입력합니다. 이 값이 변경되면 기존 로그인 세션은 모두 무효화됩니다.
예시 파일의 안내 문구를 그대로 사용하면 안 되며, 실제 생성 결과는 최소 32자 이상이어야 합니다.
운영에서는 REDIS_URL을 반드시 설정하세요. 운영 빌드에서 이 값을 생략하면 안전하지 않은 메모리 세션으로 대체하지 않고 요청을 거부합니다. 개발 환경에서만 Redis 없이 단일 프로세스 메모리 저장소를 사용할 수 있습니다.
Redis를 로컬에서 실행 중이라면 .env를 다음 형태로 설정합니다.
REDIS_URL=redis://127.0.0.1:6379의존성을 설치하고 운영 빌드 후 서버를 실행합니다. npm start는 먼저 빌드가 완료되어 있어야 합니다.
npm install
npm run build
npm start브라우저에서 다음 주소를 확인합니다.
- Webmail 화면:
http://localhost:3001 - 실제 IMAP 로그인:
http://localhost:3001/login
코드를 수정하면서 확인할 때만 개발 서버를 사용합니다.
npm run devDocker Compose는 Webmail과 Redis만 실행합니다. Compose 설정이 컨테이너 내부 Redis 주소를 자동으로 적용하므로 별도의 애플리케이션 DB 설정은 필요하지 않습니다.
REDIS_URL=redis://127.0.0.1:6379실행:
docker compose up -d --build상태 확인:
docker compose ps
docker compose logs -f webmailDocker 환경에서는 보안을 위해 Webmail 포트가 127.0.0.1:3001에만 열립니다. 외부 서비스는 Nginx를 통해 HTTPS로 연결하는 구성을 권장합니다. 실제 도메인·인증서·다른 서비스 경로가 포함된 nginx/는 서버 전용 설정이므로 Git에서 제외됩니다.
실시간 알림 SSE 경로에는 프록시 버퍼링과 압축을 끄고 충분한 읽기 제한 시간을 설정해야 합니다. 일반 요청보다 이 location을 먼저 선언하고, 적용 전 sudo nginx -t로 검사하세요.
location = /api/mail/events {
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
gzip off;
proxy_read_timeout 1h;
}/login에서 테스트 이메일 계정으로 로그인합니다.- 로그인 성공 여부와 Redis 세션 생성을 확인합니다.
- Inbox, Sent, Drafts, Junk, Trash 폴더가 실제 서버와 맞는지 확인합니다.
- 메일 목록의 보낸 사람, 제목, 날짜, 읽음 상태를 비교합니다.
- 테스트 메일을 열어 HTML/텍스트 본문과 첨부파일 정보를 확인합니다.
- 내부 계정으로 테스트 메일을 발송한 뒤 Sent 폴더와 수신 결과를 확인합니다.
- 답장·전체답장의 스레드 연결과 전달 메일의 원본 첨부파일을 확인합니다.
- 작성 중 2초 뒤 Drafts 폴더에 자동 저장되고 다시 열어 편집되는지 확인합니다.
- 50개가 넘는 폴더에서 다음 페이지와 서버 검색 결과를 확인합니다.
- 외부 계정 발송, 한글 제목과 첨부파일은 별도로 검증합니다.
GET /api/health: 로그인 없이 애플리케이션과 세션 저장소 준비 상태만 확인합니다.GET /api/mail/health: 로그인 세션으로 IMAP NOOP와 SMTP 연결 검증을 실행하고 각각의 지연 시간을 반환합니다.- Docker Compose는
/api/health를 30초마다 확인합니다. - 운영 로그의
imap_operation_timing,smtp_send_timing,smtp_send_failed이벤트로 작업별 대기·처리 시간을 확인할 수 있습니다.
메일 원본 백업은 이 애플리케이션이 아니라 Dovecot 저장소를 기준으로 구성해야 합니다. Maildir 또는 실제 mail_location, /etc/dovecot, /etc/postfix, /etc/rspamd, DKIM 개인 키를 권한과 함께 백업하고 별도 환경에서 정기적으로 복원 시험을 하세요. Redis에는 로그인 세션만 있으므로 Redis 백업만으로 메일은 복구되지 않습니다.
- IMAP 호스트와 포트가 서버에서 접근 가능한지 확인합니다.
- 아이디만 입력했다면
.env의MAIL_DOMAIN이 실제 메일 도메인과 같은지 확인합니다. - 전체 이메일 주소를 입력했다면 해당 주소가 실제 IMAP 사용자명인지 확인합니다.
- Dovecot 인증 방식이 일반 비밀번호 로그인을 허용하는지 확인합니다.
- TLS 인증서의 호스트명이
IMAP_HOST와 일치하는지 확인합니다.
- SMTP 인증 계정이 IMAP 계정과 동일한지 확인합니다.
- 587/STARTTLS와 465/SMTPS 설정을 혼동하지 않았는지 확인합니다.
- Postfix가 현재 Webmail 서버 또는 Docker 호스트의 연결을 허용하는지 확인합니다.
.env의IMAP_HOST와SMTP_HOST에127.0.0.1을 사용하지 않았는지 확인합니다.- 호스트 방화벽과 Dovecot/Postfix listen 주소를 확인합니다.
- 운영 서비스 설정이나 방화벽을 변경하기 전에는 반드시 영향 범위를 검토하세요.
다음 항목은 서버 환경마다 달라 자동 적용하지 않으며 운영 환경에서 확인해야 합니다.
- 실제 Roundcube 버전과 DB 스키마 확인
- 실제 IMAP special-use 폴더 매핑
- 주소록·identity·사용자 설정 마이그레이션
- 25MB보다 큰 첨부파일을 위한 완전한 스트리밍 업로드 전환
- 다중 Webmail 인스턴스 운영 시 IMAP IDLE 전용 worker와 Redis 이벤트 팬아웃
- Dovecot/Postfix/Rspamd 설정 및 Maildir 백업·복원 시험
- MIME fixture, IMAP/SMTP 통합, E2E, 보안 테스트 확대