2026년 3월 31일
메일 제보에서 직접 업로드로
이메일 사진 제보를 이용자 직접 업로드로 전환.
도입
오락실 데이터
#오락실문서
eric

Beta에서 오락실 사진을 보내려면 이메일에 파일을 첨부해야 했다. 운영자는 메일을 확인하고 첨부를 내려받은 뒤 서비스에 직접 등록했다. 사진을 보여 주는 갤러리는 있었지만 이용자가 파일을 올리는 경로는 아직 마련되지 않은 셈이다. Delta에서는 사이트에서 직접 사진을 받도록 바꾸고, 그동안 메일로 받은 자료도 새 저장 방식으로 옮겼다.

이메일 사진 제보와 저장 공간

파일을 선택하는 화면만 놓고 보면 직접 업로드는 간단해 보인다. 그러나 사진은 한 번 받고 끝나는 자료가 아니다. 새 제보가 들어올 때마다 저장 공간을 사용하고, 이용자가 갤러리를 열 때마다 전송량이 발생한다. 따라서 업로드를 열기 전에 계속 쌓이는 파일을 어디에 두고 어떻게 제공할지부터 준비해야 했다. 이 과정에서 S3를 연결하는 방법과 많은 파일을 운영하는 방식을 함께 익혔다.

S3는 파일을 객체 단위로 보관하는 저장소다. 서버의 디스크에 사진을 계속 쌓는 대신 외부 저장 공간을 연결해 사용할 수 있다. 다만 파일의 보관 위치만 바꾼다고 서비스가 완성되지는 않는다. 메일을 보던 운영자가 알고 있던 '어느 오락실의 사진인지, 누가 보냈는지'를 이제 서버가 자료로 남겨야 하기 때문이다.

Mermaid
flowchart TD
    accTitle: 메일 접수에서 사이트 업로드로 전환하기
    accDescr: 종전에는 운영자가 메일 첨부를 수동 등록했다. 직접 업로드는 이용자의 파일을 서버가 받아 저장소와 문서 기록에 연결한다.
    U[이용자 사진] --> M[이메일 제보]
    M --> O[운영자의 확인과 수동 등록]
    U --> W[사이트 직접 업로드]
    W --> B[서버의 파일 검사와 기록 생성]
    B --> S[S3 파일 보관]
    B --> R[오락실과 작성자 연결]

사진 업로드와 S3 저장소

S3 연결에는 버킷이라는 저장 공간을 먼저 마련한다. PocketBase의 파일 저장 설정에 버킷 이름, 리전, 엔드포인트와 접근 자격 정보를 넣고 S3 저장을 활성화하면, PocketBase의 파일 시스템이 이 공간에 파일을 보관한다. 로컬 저장소를 쓸 때와 같은 파일 처리 기능을 거치므로 화면이 저장소 주소나 비밀 키를 직접 다룰 필요는 없다.

이용자가 사진을 고르면 브라우저는 오락실 ID와 파일을 multipart 형식으로 서버에 보낸다. multipart는 일반 입력 값과 파일을 한 요청에 함께 담는 방식이다. 서버가 파일 내용을 검사하고 사진 레코드의 파일 필드에 저장하면 실제 파일 보관은 설정된 PocketBase 저장소가 처리한다. S3 자격 정보를 브라우저에 보내 직접 버킷을 조작하도록 하지는 않았다.

저장 설정 후에는 사진을 올리는 것부터 상세 화면에서 다시 여는 것까지 확인해야 한다. 데이터베이스에 남은 파일 이름과 S3의 실제 객체 위치가 맞지 않으면 업로드가 끝나도 갤러리에는 표시되지 않기 때문이다. 또한 사진 파일용 저장 설정과 데이터베이스 백업 설정은 각각 준비해야 한다. 사진을 저장할 수 있다는 사실만으로 DB 백업까지 구성되는 것은 아니다.

업로드한 사진의 공개 절차

여러 사진을 한 번에 올리는 상황을 생각해 보자. 일부 파일은 저장됐지만 한 장은 실패할 수도 있다. 업로드 API인 /arcade/photo/upload는 성공한 파일마다 arcade_photo_atoms 레코드를 만들고, 오락실과 작성자 및 파일을 연결해 비공개로 보관한다. 응답에는 성공한 레코드 ID와 실패한 파일을 나누어 담아 이용자가 실제 처리 결과를 확인할 수 있게 한다.

사진을 모두 전송한 뒤에는 문서에 사용할 목록을 정한다. /arcade/photo는 선택된 사진이 해당 오락실의 것인지 확인하고 공개 상태로 바꾼다. 이어 ID 목록을 담은 arcade_photo 레코드를 만들고 현재 사진 참조와 이력을 함께 갱신한다. 이 확정 과정을 데이터베이스 트랜잭션으로 처리하면 목록 일부만 바뀐 상태로 문서가 남는 일을 막을 수 있다. 업로드한 사진 중 공개할 것을 고를 수 있는 이유도 이 두 단계에 있다.

Mermaid
sequenceDiagram
    accTitle: 파일 보관과 문서 공개의 두 단계
    accDescr: 업로드는 비공개 사진 기록을 만들고, 목록 저장은 해당 장소의 사진을 확인해 공개 목록과 현재 참조를 확정한다.
    participant U as 이용자
    participant B as 서버
    participant S as 파일 저장소
    participant D as 데이터베이스
    U->>B: 사진 파일 업로드
    B->>S: 파일 보관
    B->>D: 비공개 사진 기록 생성
    B-->>U: 성공 ID와 실패 항목
    U->>B: 선택한 사진 ID 목록 저장
    B->>D: 소속 확인·공개 전환·현재 목록과 이력 갱신
    B-->>U: 문서 반영 결과

메일 사진의 이관과 출처

직접 업로드를 도입하면서도 기존 메일 제보는 이어서 사용할 필요가 있었다. 메일에는 파일뿐 아니라 대상 오락실과 제보자, 받은 시각이 함께 남아 있다. 사진만 복사하면 누구의 어떤 제보였는지 잃고, 메일 정보만 옮기면 화면에 보여 줄 파일이 없는 상태가 된다.

대상 메일의 첨부와 정보는 Mail AppleScript로 추출했다. ArcadeID는 장소, UID는 제보자, Received_at은 원래 접수 시각을 연결하는 데 사용했다. 사진별 레코드와 그 ID를 모은 묶음을 만들고, 파일은 컬렉션 ID와 사진 레코드 ID 및 파일 이름에 맞는 경로로 준비해 S3 객체와 연결했다. 과거 제보가 이관일에 새로 제출된 것처럼 보이지 않도록 접수 시각도 유지했다.

같은 메일을 다시 처리하면 사진이 중복 등록될 수 있다. 이관 스크립트는 오락실, 접수 시각, 제보자와 파일 이름 목록을 기존 자료와 비교해 같은 제출을 걸러낸다. 다만 이 비교는 이미 넣은 자료를 찾는 방법이다. 사진 속 오락실이나 제보자의 연결이 맞는지는 원래 메일의 내용과 대조해야 한다.

Mermaid
flowchart TD
    accTitle: 기존 사진의 파일과 출처를 함께 옮기기
    accDescr: 메일에서 추출한 파일을 저장 항목과 연결하고 장소·제보자·원래 접수 시각을 서비스 기록으로 보존한다.
    M[메일 첨부와 제보 정보] --> E[AppleScript 추출]
    E --> F[파일과 저장 경로 준비]
    E --> I[장소·제보자·접수 시각]
    F --> S[S3 보관 항목]
    I --> R[사진 기록과 사진 묶음]
    R --> S

직접 업로드로 운영자가 모든 메일의 파일을 다시 등록하던 과정을 줄일 수 있었다. 대신 이용자가 파일을 올렸지만 공개 목록을 저장하지 않은 상태도 생길 수 있어, 파일 보관 결과와 문서 반영 결과를 각각 다뤄야 한다. 파일이 존재하더라도 장소 정보가 잘못 연결되면 엉뚱한 갤러리에 나타날 수 있다. S3를 마련한 뒤에도 사진의 소속과 공개 상태를 관리하는 작업은 계속 필요하다.

참고: 뮤즈캣 엔지니어링 기록.

뮤즈캣
리듬게이머의 최고의 파트너