계약 7절(API 둘 · 글 파일 규칙)과 머리말의 달력 날짜 예외, 웹 · 앱 디자인 README 의 업데이트 내역 절, 세 스택 스킬 행을 더했다.
보드의 목록 최대 개수를 구현(100)에 맞추고, 편집 도구가 보이지 않는 글자의 이스케이프를 실제 글자로 푼 일을 공통 규칙에 적는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 주소록 가져오기 창의 미리 보기가 1366×768 · 1440×900 에서 잘리고 창도 스크롤하지 않았다 — 창(<dialog>)은 flex 열이라 overflow 를 건 세 묶음 상자가 내용보다 작게 눌렸다. 상자에 flex-shrink: 0 을 두고 가져오기 · 내보내기 창은 창 안이 스크롤한다
- 서명 위 빈 줄에 한글을 조합해 쓰면 띄어쓰기를 칠 때까지 빈 줄이 돌아오지 않았다 — 조합이 끝나면(compositionend 뒤 ProseMirror 가 조합을 마무리한 다음 차례) 편집기가 스스로 빈 줄을 넣는다. 조합 중에 임시 저장한 뒤 편집기가 넣은 빈 줄은 바꿈으로 세지 않는다
- 디자인 README · vue3 스킬에 두 가지를 적는다(모달 창의 flex 열 — 창 바로 아래 칸에 overflow: hidden 을 걸면 flex-shrink: 0 을 함께)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 폭 360 에서 검색 칸이 상단 바 밖으로 넘쳐 화면이 옆으로 밀렸다(14단계부터) — 메일 검색 칸 · 비활성 통합 검색 칸의 최소 폭을 0 으로 둔다
- 읽기 칸 도구 줄이 이름을 숨기는 폭을 570 · 595 에서 580 · 605 로 올린다 — 실측한 필요 폭(569.19 · 594.83)에 여유가 1px 도 없어 글자 · 여백이 조금만 늘어도 두 줄로 꺾였다
- 잘못 놓은 파일 무시를 메일 화면(MailLayout)에서 앱 틀(AppShell)로 옮긴다 — 주소록 · 설정에 놓은 파일은 브라우저가 열어 웹메일을 떠났다. 쓰기 화면 · 서명 설정은 지금처럼 받는다
- 받은편지함 정리를 켠 차단 뒤에는 다음 메일 대신 목록으로 간다 — 다음 메일이 같은 보낸 사람의 것이면 열자마자 읽음이 되고 곧 정리가 스팸메일함으로 옮겼다
- 모달 창의 제목 · 글은 낱말 가운데서 줄을 바꾸지 않고("지울까/요?") 띄어쓰기 없는 긴 글만 아무 데서나 끊는다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
루트 README · CLAUDE.md · 점검 목록이 아직 "풀스택 모노레포 템플릿"으로 소개하고 있었다 — 사내 그룹웨어(메일 운영 중)에 맞게
지금 상태 · 구성(메일 엔진 · 리졸버 · ClamAV · 운영 스크립트 포함) · 문서 지도 · 디렉터리 구조로 다시 쓰고,
백엔드를 호스트에서 npm ci 하라던 옛 안내(컨테이너 안에서만 — 규칙과 반대)를 뺐다.
docs/roadmap.md 에 메일 1~16단계 상태(16단계 첫 항목: 주소록 가져오기 · 내보내기) · 공통 기반 · 2부 · 앱 · 추후 후보 모음을 둔다.
backend · frontend README 는 프레임워크가 만든 영어 안내 대신 이 저장소의 실행 · 검사 · 참고 문서로,
규칙 · 스킬 · 역할 문서의 "템플릿" 표현("템플릿 기본 제공 골격" 등)은 "기본 골격"으로 바꿨다(nginx 설정 템플릿은 그대로).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 2절에 메일함 옮기기 · 하위까지 지우기 · 받은편지함 정리 · 지금 보내기(결과를 모르는 실패와 알려진 한계까지),
디자인 README 에 15단계 절과 구현하며 정한 보완, nestjs · vue3 스킬 표에 새 파일과 잠금(여러 대로 늘릴 때 옮길 것).
14단계 "미룬 것"에서 15단계에 한 것(대화째 끌기 · 모두 펼치기 · 줄 머리 답장)을 뺐다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 1절 "휴대폰 앱 받기"와 6절 운영 도구 명령, 서버 B 에 APK 올리는 차례와 직원 안내(링크 없이),
앱의 서명 키 · release 빌드 · 올리기 전 확인, 웹 화면 절, 스킬 표, App1Shell 보드의 앱 정보 줄.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
이 PR 이 앱 서버 PR 뒤에 빨리 감기로 합쳐지고 CI 가 합친 모습 그대로를 시험하게 한다.
계약 1절의 앱 절은 앱 서버 쪽이 들고 있고 이 브랜치는 2절 줄만 더했다 — 충돌 없음.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
이 PR 이 문구 다듬기 PR 뒤에 빨리 감기로 합쳐지고 CI 가 합친 모습 그대로를 시험하게 한다.
충돌 넷은 모두 설정 › 로그인된 기기의 글이다 — 앱 쪽은 앱 세션이 함께 보여 "브라우저"를 "기기"로,
다듬기 쪽은 같은 안내 문장을 규칙 9 로("여기에 보이지 않습니다" → "이 목록에 없습니다") 바꿨다.
뜻은 앱 쪽 · 문장은 다듬은 글로 풀었고(account-security.ts · LoginSessionList.vue 주석 · 디자인 README),
시험은 두 쪽이 더한 상수를 모두 남기고 글자 그대로 견주던 줄을 상수로 바꿨다(AccountLoginHistoryPage.spec.ts).
합친 뒤 backend tsc · lint 0 · 단위 3,701 · e2e 333, frontend type-check · lint 0 · 단위 2,774 통과.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
14단계 검색 브랜치에 main(#16 — 백엔드 의존성 넷 · 유예 표)을 합친 판을 받는다.
이 PR 이 14단계 PR 뒤에 빨리 감기로 합쳐지고 CI 가 합친 모습 그대로를 시험하게 한다. 충돌 없음.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#16(백엔드 의존성 넷 · 유예 표)이 갈래점 ba80956 뒤에 main 에 들어갔다 — 이 PR 이 빨리 감기로 합쳐지고 CI 가 합친 모습 그대로를 시험하게 한다. 충돌 없음.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
서버는 N 을 seen_at 의 초부터 같은 초를 넣어 센다(받은 시각이 초 단위). 앱을 보는 중 새 메일 알림을 받자마자
봤음을 보내면 seen_at 이 그 메일과 같은 초가 되어, 목록으로 보고 열지 않은 그 메일이 다음 알림에 다시 세어졌다
(2026-11-08 실기기 — 한 통이 더 왔는데 "새 메일 2통"). 그 봤음만 받은 때 + 1.5초와 30초 간격 가운데 늦은 때에 보낸다.
앱으로 돌아올 때 · 기다리는 동안 뒤로 갈 때는 그대로다. 고친 뒤 실기기에서 봤음이 2초 뒤에 갔고 다음 알림은 "새 메일 1통"이었다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
엔진 연결을 붙잡자마자 메일이 오면 따라잡기와 그 메일의 새 메일 신호가 함께 보내 알림이 두 번 갔다(커밋 전 다시 리뷰 N1).
붙잡은 뒤 신호를 이미 받았으면 따라잡기를 건너뛰고, 따라잡기 뒤 간격 끝의 보내기는 따라잡기가 센 뒤에 받은 메일이 있을 때만 보낸다
(기록 저장이 실패해도 겹치지 않게 메모리로 본다). 옛 주석의 "로그인이 저절로 끝남"을 끝난 까닭 표로 고치고 계약 · 스킬의 문장도 맞췄다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 1절 끝의 옛 메모(/api/push-devices · { minVersion, latestVersion, storeUrl })를 확정한 경로 · 모양 · 규칙으로 바꾸고
(N · 간격 · 따라잡기 · 엔진 after 의 초 경계 · "알림이 멈췄습니다"를 보내는 까닭 표 · 실패 처리 · 실기기로 확인할 것),
"클라이언트 — 앱"에 앱이 실제로 부르는 때를 적었다. 2절 실시간 알림의 붙잡기와 6절 퇴사 마무리 목록도 더했다.
nestjs · docker 스킬과 env · 보안 · 로그 규칙에 FCM 키 자리 · 바깥 연결 · 가릴 값을 더했다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
앱 디자인 README 에 "새 메일 알림 · 업데이트 · 앱 아이콘" 절(허락 묻기 · 설정 줄 상태 · 받기 · 누르기 · 로그아웃 차례 ·
"알림이 멈췄습니다"를 보내는 때)을, flutter README 에 Firebase 설정 파일 · 아이콘 만들기 · Play 데이터 보안 신고를,
flutter 스킬에 새 행을, 계약 2절 "새 메일 확인"에 앱이 알림을 받았을 때 한 번 묻는 줄을 더했다.
보드 App1Push 의 ⑧ 칸 설명은 "알림이 멈췄습니다"를 넓힌 결정대로 고쳤다(캔버스 v66).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
앱 새 메일 알림(푸시)의 서버 일을 앱 세션과 14단계 위에서 하려고 합친다.
겹친 곳은 두 곳이다. auth.module.ts 는 import 둘을 함께 두고,
AuthService.login() 은 enrollAfterLogin(14단계)을 남긴 뒤 recordSignIn(앱 로그인)으로 기록한다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
조건 시트 · 날짜 · 조건 줄 · 낱말 넘김 · 시트의 세 모양 · 가림막처럼 구현하며
정한 것과 웹과 다른 점(검색어도 조건도 비면 찾기 전 화면 · 시트 안 낱말 알림 ·
날짜는 오늘까지)을 적고, 1차 문서의 "조건으로 찾기는 2차"와 미룬 것을 고친다.
조건 칸은 14단계 서버부터 받는다는 것과 알려진 한계 둘(라틴 글자의 "나" 견주기 ·
한 스크롤 모양에서 끌어 내려 닫기)도 남긴다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
디자인 README 「화면 문구」에 규칙 9(화면이 하는 일을 설명하지 않는다) · 10(번역투 ·
설계 말을 쓰지 않는다)과 11-07 적용 줄을 더하고, 옮겨 적은 화면 글 · 오류 사전 글을
새 글로 맞춘다(계약 · vue3 스킬 · 에러 코드 규칙 표 포함). 디자인 보드의 글은
그대로다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
앱 디자인 README 「검색」 절, flutter 스킬 표 · 함정 두 줄 · CI 경로, 계약 2절
"검색 — 14단계"의 앱 클라이언트 줄과 목록 항목의 searchHighlight 칸이 늘 있다는
줄, 두 표본 표 머리말(앱 시험이 백엔드 표를 읽는다)을 적는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
같은 대화 칸의 안 읽은 줄, 패널 [조건 지우기], 미리 보기 강조의 글자색을 "14단계
구현하며 정한 보완"에 적는다. 긴 대화의 [모두 펼치기] · 펼친 줄 머리의 [답장]은
미루기로 해 "처음 판에서 미룬 것"에 둔다(2026-11-07 사용자).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
앱 검색 화면(App1Search)은 계약 2절 "검색 — 14단계", 웹 글 원본과
백엔드 표본 표(backend/test/search-text-samples.ts)를 짝 시험으로 읽는다.
feat/mail-search-index 는 아직 main 에 없어서, 갈래점이 ba80956 인 앱 브랜치에
먼저 합친다. 14단계가 main 에 들어가면 이 브랜치를 main 위로 옮긴다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
앱 디자인 README 「쓰기」(첨부 · 나가기 · 알려진 한계 · 실기기에서 볼 것),
flutter 스킬(받은 파일 · 고른 파일 · 형식 줄, 메일 쓰기 줄, Kotlin 을 고쳤을 때의
apk 빌드, 시험 함정), 백엔드 형식 표(ATTACHMENT_TYPES)에 앱 형식 표와의 짝 주석.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 2절 "서식 본문"에 앱이 만드는 모양(서명 경계 div · 글로 만든 원문
인용 · 다시 열기 조건), 앱 디자인 README 「쓰기」에 구현하며 정한 것 ·
알려진 한계 · 실기기 점검, flutter 스킬에 쓰기 화면 줄과 위젯 · 시험
함정을 적는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 2절에 본문 강조 · 미리 보기 조각 고르기 · 같은 대화 줄 차례 · 설정
다시 받기, 디자인 README 14단계 보완 절에 바뀐 글 · 크기 · 알림과 알려진
한계, nestjs 스킬에 찾기 GIN 하나 규칙, vue3 스킬에 새 도우미를 적는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
앱 디자인 README 「읽기」에 짜임 · 두 번째 정화 · 그림 한도 · 첨부 형식 막기 ·
붙잡힌 메일 · 알려진 한계 · 실기기 점검을, 「쓰기」에 mailto 숨은참조를
펼쳐 보일 것을 적는다. flutter 스킬 골격 표에 읽기 화면 · 받은 파일 줄과
인터셉터의 실패 본문 규칙을 더한다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 2절 "대화 — 14단계"에 묶은 목록의 클라이언트 줄(첫 진입 짐작 · 맞추기 ·
할 일 메일 · 줄의 별 · 옮기기 뒤 다시 받기 · 알려진 한계), 디자인 README 에
"14단계 구현하며 정한 보완 — 목록 대화로 묶어 보기", vue3 스킬에 골격 줄 ·
밀도 예외 · 줄 기준 도우미 규칙을 적는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 앱 디자인 README: 고르기(줄 누르기 · 바뀌는 단추 · 영구 삭제 · 읽음 뒤 끝내기 · 선택 풀기),
붙잡힌 메일, 알림 두 갈래, 서랍, 30일 줄 · [찾기] 준비 중, 새 메일 확인과 병합 규칙(웹과 다른 점),
크기 맞춤, 메일함 이름 차례
- flutter 스킬 골격 표: 메일 목록 · 앱 틀 줄(틀은 Scaffold 가 아니다 · ShellBarHidden · 알림 두 갈래 ·
생명주기 안 프로바이더 · 가짜 서버의 한 프레임 응답)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- nestjs: Swagger 는 쿠키와 Bearer 둘 다, 인증 가드가 Bearer(앱 토큰만)와 쿠키를 읽는 차례,
직원 인증 · 계정 보안 줄에 앱 경로 · client 칸 · 앱 세션의 유예, JWT 절을 사실대로(12단계부터
끊은 세션은 sid 로 바로 막고 13단계부터 rst 토큰만 세션 표를 읽는다)
- vue3: 계정 보안 줄에 로그인된 기기 — 웹 · 앱은 client · method 로만, 앱 이름 MOBILE_APP_NAME
(짝: backend APP_BROWSER_NAME)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 앱 디자인 README: 단계마다 실패를 보이는 곳(표), 안내와 오류 함께, 세션 끝은 상자만,
[다른 계정으로 로그인]을 막는 때, 2단계 켜기의 처음 초점 · 켠 뒤 알림, 다른 곳에서 켠 경우,
주소 칸 자판, 글자 크기 맞춤, 새 글, 알려진 한계(인증 앱에 다녀오는 사이 앱이 정리되면 새 키 ·
최근 앱 미리 보기 · 클립보드 · iOS)
- flutter 스킬 골격 표에 로그인 화면 줄(노티파이어 제한 단계 API · 알림 끄는 묶음 · url_launcher
쓰는 규칙 · KeepAllText)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- flutter 스킬: 기본 제공 골격을 이번 앱 기반으로(인증 인터셉터 · 토큰 보관 · 세션 노티파이어 ·
모듈 틀), 새 모듈 붙이는 법, 비밀이 든 요청의 실패 예외, 자동 다시 하기 끔, Flutter 판
- 보안 · 에러 코드 · 코딩 규칙: 앱은 리프레시 토큰을 본문으로 받아 보안 저장소에(kRefreshTokenKey),
세션 만료 승격은 갱신 뒤 · 스스로 로그아웃한 뒤의 401 은 승격하지 않는다(웹과 같다)
- mobile-dev 역할: 작업 순서 · 완료 기준(apk 빌드 포함)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
같은 마이너의 최신 패치 3.41.9(Dart 3.11.5 · 2026-04-30)로 개발 PC 와 CI 를 함께 올린다
(2026-11-06 사용자 확인 — 개발 PC 의 Flutter 는 다른 프로젝트도 함께 쓴다). CI 는
`flutter pub get --enforce-lockfile` 로 lock 과 어긋나면 실패한다. docker 스킬의 이미지 버전 표에
Flutter SDK 줄과 함께 고치는 곳을 둔다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 계약 2절 "대화 — 14단계"에 읽기 화면 칸의 클라이언트 줄: 100통 · 이어 묻기 5번 · [더 보기],
통 수, 알림 없는 실패와 404 면 칸 없음, 다시 받는 때와 10초 간격, 펼친 줄의 읽음
- 디자인 README "14단계 구현하며 정한 보완 — 같은 대화": 크기, 보드와 다르게 한 것
(기존 바탕 토큰 · h3 · 본문 14px · 펼친 줄의 안내), 보드에 없던 것, 갈 곳 · 초점 · 다시 받기
- vue3 스킬에 같은 대화 줄 · 스토어 구성 · 밀도 예외 재사용을 더한다
- docker 스킬의 요청 수 한도 안내에 메일을 열 때마다 부르는 같은 대화 경로를 더한다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 계약 2절 "검색 — 14단계"에 클라이언트 줄: 화면 주소(조건 이름 · since · until · attachment),
손으로 고친 주소의 검사, 읽기 화면에 넘기는 다섯 칸, 이어 찾기 5번, 준비 상태를 모를 때,
보낸 사람이 쓴 <mark> 의 한계
- 디자인 README "14단계 구현하며 정한 보완 — 검색": [조건] 32 밀도 예외 재사용, 패널 · 조건 줄 ·
결과 없음 · [더 찾기] · 낱말이 너무 많은 주소의 글, 목록 보기는 행 38 에 제목만 강조,
결과 줄의 별 · 고르기 칸(3단계 여러 통 처리), 강조 색 토큰
- vue3 스킬에 14단계 검색 줄과 밀도 예외 재사용을 더한다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- "대화 — 14단계" 절: 기준(basis — 색인 · 엔진), 대화에 드는 메일, 같은 대화 ·
대화로 묶어 보기의 쿼리 · 응답(ConversationSummary — messageIds 최신 500 ·
pendingSendIds), 엔진 방식의 상한과 어림이 되는 줄, 빈 쪽에도 커서가 오는 때, 알려진 한계
- 보내기 · 읽기 설정에 groupByConversation(처음 false — 서버는 저장만)
- API 표에 검색 조건 · 강조 쿼리, 같은 대화 · 묶은 목록 · 검색 색인 경로를 더한다
- nestjs 스킬의 메일 검색 색인 줄에 3차(대화 키 · 보낸 사람 칸 · 엔진 상한)를 적는다
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
쿼리 일곱 · 합친 낱말 8개 · 찾는 규칙(부분 일치 · 구절 · 한 글자) · 목록과 읽기 화면의
searchHighlight · hasNext 인데 빈 쪽 · total null 이면 화면이 통 수를 숨기는 것 · 강조 응답은
보이기만(인용 · 인쇄에 쓰지 않음)을 적고, 3단계의 알려진 한계는 색인을 채우는 동안에만
남는다고 고쳤다. nestjs 스킬의 mail-search 줄도 2차에 맞췄다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1만 통 측정에서 색인 찾기는 표 · 색인이 메모리에 있을 때만 1초 안이었고 캐시가 비면
3~8배 느렸다(사용자 결정 2026-11-05). 8GB 서버의 한도 합은 6.65GB · 백업 때 7.65GB 다.
복원 연습 때는 8.15GB 로 8GB 를 넘는다 — 사실만 적고 다룰 방법은 따로 정한다.
mail/README 에 운영에 올리는 차례(db 만 먼저 up · 한도 · shared_buffers 확인)를 적었다.
docker 스킬의 연습 주기를 기준 문서(README — 한 달에 한 번)에 맞췄다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
계약 2절에 "검색 색인 (GET /api/search-index)" — 색인에 드는 것 · 만드는 때 · 밤 채우기 ·
맞추기 · 조회지만 상태 줄을 만드는 동작(멱등)을 적었다. 로그 규칙에 개발 환경도 DB 쿼리
매개변수를 남기지 않는다는 줄, nestjs 스킬에 mail-search 모듈 줄과 Redis 로 옮길 상태를 더했다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
올린 넷을 표에서 빼고, 남은 운영 권고(swagger 아래 js-yaml · typeorm 아래 glob · minimatch)와
개발 전용 묶음의 다시 볼 시점을 적는다. multer override 는 platform-express 를 v12.3 이상으로
올릴 때 지운다(nestjs 스킬도 같이). npm audit fix 를 통째로 돌리지 않는 까닭을 함께 적는다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README 에 「화면 문구 — 사용자에게 말하듯」 절(규칙 여덟과 예시)을 두고, README 가 옮겨 적은 화면 문구를
새 글로 맞춘다. vue3 스킬 점검 목록에 문구 줄을 더한다. 보드(*.dc.html)의 글은 11-05 전 것으로 둔다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
문서만 바꾼 PR(#8, run 138)의 CI 에서 할당이 많은 성능 단언 셋이 넘쳤다(받는 사람 맞추기 3.29초 · JSON 모양 1.35초 · DTO 2.94초).
프로세스 user + system 시간에는 V8 의 GC · JIT 도우미 스레드와 페이지 폴트 몫이 들어가 기계 사정을 따라 크게 흔들린다.
같은 Linux 컨테이너에서 전체 테스트 16회: 프로세스 user + system 은 4회 넘침, 스레드 user 는 0회.
막으려는 옛 경로(제곱)는 거의 user 시간이라 그대로 잡힌다(받는 사람 맞추기 옛 방법 51~70초).
받는 사람 맞추기 한도는 CI 쪽 값(0.17~0.25초)의 10배쯤인 2.5초로 올렸다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
개발 엔진 실측(2026-11-03, 0.16.27): 큰 메일 한 통을 받으면 엔진 메모리가 그 크기의
5.5배쯤 는다(IMAP APPEND · SMTP 받기 같다). SMTP 받는 한도(x:MtaStageData 의
maxMessageSize, 기본 100MiB)는 x:Email 한도와 따로라, 기본값 · 1g 에서 바깥 104MB
한 통이 984/1024MiB 였다(운영은 106.4MB IMAP APPEND 로 OOM).
사용자 결정 — 메일 컨테이너 메모리는 1g 그대로 두고, setup 이 받는 한도를 50MiB 로
맞춘 뒤 리로드 뒤 EHLO 의 SIZE 로 실제로 걸렸는지 본다(저장 때 줄만으로는 리로드
실패를 못 거른다 — 리뷰). IMAP · 메일 크기 한도는 imapsync 이전 동안 운영자가 잠시
올리므로 setup 이 바꾸지 않고 기본보다 크면 [주의] 만 낸다.
README 에 잰 값 · 정책 · 이전 뒤 되돌리는 차례(세 값 → setup [확인] → 1g)와
문제 해결 줄, docker 스킬에 한 항목.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NCP 버킷에는 따로 키가 없다(사용자가 버킷을 만든 뒤 키를 찾지 못했다). 키는 서브 계정의
Access Key 탭에서 만들고 Secret Key 는 그때만 보인다 — 차례를 README 에 적었다.
"이 버킷만 쓰는 하위 계정"은 NCP 문서로 뒷받침되지 않는다(관리형 정책은 Object Storage 전체,
사용자 정의 정책에도 버킷을 고르는 칸이 문서에 없다) — README · ops.env 예시 · 체크리스트를
"Object Storage 만 쓰는 서브 계정 · 같은 계정에 다른 버킷을 두지 않는다"로 바로잡았다.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 스킬 표: nestjs(제한된 세션 · 2단계 필수 정책 · 받은편지함 알림 · 운영 도구 · 15초 동기화 · 만료 계정의 엔진 사실), vue3(제한된 세션 화면 · 로그인 틀 단계 · 유예 줄 · 메일 프로그램 연결 · 30일 줄),
docker(이미지 표 — PostgreSQL · restic · shellcheck, 자동 설정 server, 호스트 systemd 백업 · 감시 절, setup 의 관리자 단단히 · 휴지통 30일, 로그 앵커 규칙, CI infra 잡).
- 규칙: security(제한된 세션 · 운영 도구 비밀 · 관리자 계정 · 알림 메일 링크 없음 · 퇴사 마무리 · 백업 비밀 · restic 의 root 예외), logging(컨테이너 로그 보존 · 실행 기록 · 자동 설정 쿼리),
env-structure(자동 설정 env · DB_IMAGE · ops.env 예외 · OPS_ALLOW_NO_TTY), error-codes(AUTH_006 ~ AUTH_010), checklist(13단계 운영 준비), release-ops 담당 범위(mail/ · ops/).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>