계정 및 시스템 설정

시스템 환경변수, 관리자 계정 관리, 이메일 · 알림 설정 운영 방법을 정리합니다.

1. 환경변수 (.env)

환경변수는 각 파드의 환경으로 주입됩니다. 운영 환경에서는 Kubernetes Secret / ConfigMap 으로 관리됩니다.

원본 참조: .env.development.example (로컬 템플릿), config/__init__.py (AppConfig)

1.1 애플리케이션 공통

의미기본값변경 시 영향
APP_ENV환경 식별자 (prd / dev / local)localDB 이름({env}_heka), Redis key prefix(hk-{env}) 결정. K8s 네임스페이스와는 무관 — K8s 네임스페이스는 cone-watcher / mongodb / valkey 고정
APP_NAME콘솔/이메일 상단 표기 이름CONE Watcher N프론트 라벨, 이메일 제목
BACKEND_URLAPI 외부 URLhttp://localhost:8000메일 링크 등
DASHBOARD_URL프런트엔드 URLhttp://localhost:5173초대/비밀번호 재설정 메일의 링크 목적지

1.2 MongoDB

의미기본값
MONGODB_HOST호스트localhost
MONGODB_PORT포트27017
MONGODB_USERNAME접속 계정
MONGODB_PASSWORD비밀번호<PLACEHOLDER>
MONGODB_DIRECT_CONNECTIONreplica set DNS 조회 건너뛰기 — 로컬 포트포워딩 접속용 옵션false (클러스터 내부 기본값). 로컬에서 kubectl port-forward 로 붙을 때만 true

연결 풀(minPoolSize=10, maxPoolSize=100)과 타임아웃은 코드 상수(common/client/database/mongo.py)이며, 환경변수로 바꾸지 않습니다.

1.3 Redis (Valkey)

의미기본값
REDIS_HOST호스트localhost
REDIS_PORT포트6379
REDIS_PASSWORD비밀번호<PLACEHOLDER>

Redis는 Dramatiq 브로커 겸 결과 저장소로만 쓰입니다. 데이터 영속성은 고려하지 않지만, 재기동 시 큐가 비어버리지 않도록 운영은 persistence 설정을 권장합니다.

1.4 NHN Cloud 리셀러 API (일반)

의미
NHN__API_PREFIXAPI 호스트 프리픽스 ({prefix}.api.nhncloudservice.com)
NHN__HMAC_SECRETHMAC-SHA256 서명용 시크릿
NHN__ACCESS_KEY리셀러 액세스 키
NHN__SECRET_KEY리셀러 시크릿 키

1.5 NHN Cloud 리셀러 API (공공)

의미
NHN_GOV__API_PREFIX공공 API 호스트 프리픽스 ({prefix}.api.gov-nhncloudservice.com)
NHN_GOV__HMAC_SECRETHMAC 시크릿 (공공)
NHN_GOV__ACCESS_KEY액세스 키 (공공)
NHN_GOV__SECRET_KEY시크릿 키 (공공)

구분 기준: nhn_account.cloud_type 필드가 NORMAL 이면 NHN__* 키를, GOV 이면 NHN_GOV__* 키를 사용합니다. 두 환경 모두 운영한다면 네 쌍 모두 채워야 합니다.

1.6 SMTP (이메일 발송)

의미기본값
SMTP_HOSTSMTP 서버 호스트
SMTP_PORT포트 (STARTTLS)587
SMTP_USERNAMESMTP 사용자명<PLACEHOLDER>
SMTP_PASSWORDSMTP 비밀번호<PLACEHOLDER>
EMAIL_SENDER발신자 이메일 주소

프로토콜은 STARTTLS (포트 587) 고정입니다. SSL(465) 사용 시 코드 수정 필요.

1.7 변경 시 재기동 대상

변경 범위재기동 대상
MONGODB_*, REDIS_*api, worker, scheduler 전부
NHN*, SMTP_*, EMAIL_SENDERworker (+ 필요 시 api)
APP_NAME, BACKEND_URL, DASHBOARD_URLapi, worker
APP_ENV주의 — DB 이름({env}_heka)과 Redis key prefix(hk-{env})가 바뀝니다. 단순 재기동으로 끝나지 않고 데이터가 새 DB로 격리됨

재기동 명령 (prd 기준 — 실제 deployment 이름은 base namePrefix: cone-watcher- + overlay namePrefix: prd- 가 합쳐져 prd-cone-watcher-* 로 배포됩니다):

kubectl -n cone-watcher rollout restart \
  deploy/prd-cone-watcher-api \
  deploy/prd-cone-watcher-worker \
  deploy/prd-cone-watcher-scheduler

1.8 스케줄러 크론 (config/envs/.env.scheduler)

스케줄러 전용 cron 표현식 파일. UTC 기준.

배포 경로 주의: 이 파일은 컨테이너 이미지에 번들되어 들어갑니다. 즉 gitops에 별도 ConfigMap/Secret으로 존재하지 않고, 소스 리포의 config/envs/.env.scheduler 를 수정 → 이미지 재빌드 → deploy/prd-cone-watcher-scheduler 이미지 태그 교체(또는 재배포) 순서로 반영됩니다. 런타임에 ConfigMap만 바꿔서 고칠 수는 없습니다.

의미 (KST)
NHN_INVOICE_TASK0 15 4 * *매월 5일 00:00 청구서 배치
UPDATE_NHN_PROJECT_LIST_TASK0 0-12/2,15,21 * * *09:00~21:00 2시간 간격 + 00:00, 06:00
CONTRACT_EXPIRATION_TASK0 0 * * *매일 09:00 계약 만료 알림
AUTO_SUSPEND_ORGANIZATION_TASK0 20 * * *매일 05:00 만료 조직 자동 중지
PARTNER_USAGE_TASK0 0 1 * *매월 1일 09:00 파트너 사용량 생성

재배포 후에는 kubectl -n cone-watcher rollout restart deploy/prd-cone-watcher-scheduler 로 적용을 확인합니다.


2. 관리자 계정 관리

2.1 역할 체계

common/utils/constants.py:UserRole 기준으로 상위 → 하위.

역할범위
ROOT시스템 최상위 (그루매틱 내부용)
SYSTEM_ADMIN시스템 관리자
ADMINMSP(파트너) 회사 관리자 — 설정/팀/청구 전체
MANAGER매니저 — 청구 작업 수행
CLIENT_ADMIN고객사 측 관리자
CLIENT_USER고객사 측 일반 사용자

상세 권한은 기능 정리 - 역할 및 권한 참조.

2.2 신규 관리자 초대 (권장 경로)

콘솔에서 초대하는 것이 기본이며, 이 방법을 우선 사용합니다.

  1. 관리자(ADMIN 이상)로 로그인.
  2. 설정 → 팀 & 권한 → 멤버 초대.
  3. 이메일·이름·역할을 입력하고 [초대].
  4. 초대 이메일(new_user 템플릿)이 발송되고, 수신자가 링크에서 비밀번호를 직접 설정합니다.

이메일이 도착하지 않는다면 §3 (이메일 설정) 점검.

2.3 부트스트랩 관리자 — DB 직접 삽입

초기 환경에 ROOT/SYSTEM_ADMIN/ADMIN 이 한 명도 없을 때 또는 이메일 경로가 막혀있을 때 사용하는 비상 경로입니다.

권장 — scripts/bootstrap_admin.py

bcrypt 해시 생성 + TSID uid 발급·충돌 체크 + 회사 존재 확인 + 이메일 중복 확인 + insertOne 을 한 번에 처리합니다. 비밀번호는 getpass 프롬프트로 입력받아 터미널 히스토리에 남지 않습니다. 사전 전제(MongoDB primary 포트포워딩 · APP_ENV · MONGODB_*)는 scripts/README.md 참조.

# Dry-run (실제 insert 안 함 — 삽입 예정 내용과 DB 이름을 먼저 확인)
python -m scripts.bootstrap_admin \
  --email admin@example.com \
  --company <COMPANY_UID> \
  --role ADMIN \
  --first-name 홍길동

# 실제 삽입
python -m scripts.bootstrap_admin ... --execute

허용 역할: ROOT / SYSTEM_ADMIN / ADMIN. 비밀번호는 §2.4 의 정책(8자+대소문자+숫자+특수문자)을 만족해야 합니다.

대안 — mongosh 로 직접 삽입

스크립트를 쓸 수 없는 환경(예: 파이썬 런타임이 없는 임시 점검 머신)에서 마지막 수단으로.

  1. 임시 비밀번호 해시 생성 — 운영 파드 또는 로컬 Python 환경에서:

    from common.utils.encryption import get_password_hash
    print(get_password_hash('<TEMP_PASSWORD>'))
    # → '$2b$12$...' (bcrypt 해시)
  2. user 문서 삽입mongosh (primary 파드 포트포워딩 필수, DB 접근 가이드 §1.2 참조):

    db.user.insertOne({
      uid: '<TSID_OR_UUID>',             // 다른 user 와 충돌하지 않게
      email: '<ADMIN_EMAIL>',
      password: '<BCRYPT_HASH>',         // 위에서 생성한 해시
      company_id: '<COMPANY_UID>',       // 소속 MSP 회사
      role: 'ADMIN',                     // 필요에 따라 SYSTEM_ADMIN / ROOT
      first_name: '<NAME>',
      last_name: '',
      phone_number: '',
      department: '',
      title: '',
      deactivate: false,
      need_change_password: true,        // 최초 로그인 시 비밀번호 변경 강제
      is_first_login: true,
      refresh_tokens: [],
      organizations: [],
      created_at: new Date(),
      updated_at: new Date()
    });
  3. 해당 이메일로 로그인 → 비밀번호 변경 유도.

주의: uid 는 애플리케이션이 TSID 로 생성합니다. 직접 만들 경우 충돌을 피하기 위해 다른 필드에서 쓰지 않은 값을 사용하세요. 가능하다면 §2.2 의 초대 경로를 복구하는 것이 우선 입니다.

2.4 비밀번호 초기화

다음 두 경로가 있습니다.

1) 사용자 스스로 재설정 (reset_password_token 플로우)

  • 로그인 화면 → [비밀번호 찾기] → 이메일 입력.
  • request_reset_password 메일이 발송되고, 링크에서 새 비밀번호 설정.
  • 재설정 링크 만료는 RESET_PASSWORD_EXPIRE_MINUTES = 30 (config/envs/.env.api).

2) 관리자가 강제 초기화

  • 콘솔: 팀 & 권한 → 멤버 선택 → [비밀번호 초기화].
  • 또는 DB 플래그로 우회:
    db.user.updateOne(
      { email: '<USER_EMAIL>' },
      { $set: { need_change_password: true } }
    );
    그리고 관리자가 API 를 통해 재설정 메일 발송을 트리거하거나, 사용자가 스스로 위 플로우를 따르게 합니다.

2.5 계정 비활성화 / 활성화

// 비활성화 — 로그인 차단
db.user.updateOne(
  { email: '<USER_EMAIL>' },
  { $set: { deactivate: true, updated_at: new Date() } }
);

// 활성화
db.user.updateOne(
  { email: '<USER_EMAIL>' },
  { $set: { deactivate: false, updated_at: new Date() } }
);

2.6 역할 변경

db.user.updateOne(
  { email: '<USER_EMAIL>' },
  { $set: { role: 'ADMIN', updated_at: new Date() } }
);

현재 로그인 중이라면 토큰이 만료되거나 다시 로그인해야 새 역할이 반영됩니다.

2.7 토큰 관련 설정 (config/envs/.env.api)

의미
ACCESS_TOKEN_EXPIRE_MINUTES60 * 2액세스 토큰 2시간
REFRESH_TOKEN_EXPIRE_MINUTES60 * 24 * 14리프레시 토큰 14일
KEEP_LOGGED_IN_ACCESS_TOKEN_EXPIRE_MINUTES60 * 24 * 5로그인 유지 모드
TEMP_TOKEN_EXPIRE_MINUTES60임시 토큰
EXTERNAL_ACCESS_EXPIRE_MINUTES30외부 링크 토큰
RESET_PASSWORD_EXPIRE_MINUTES30비밀번호 재설정 링크
TRIAL_PERIOD14시험판 기간(일)
TRIAL_ENABLEDFalse시험판 활성화 여부

3. 이메일 · 알림 설정

3.1 SMTP 설정 확인

위 §1.6 의 SMTP_* / EMAIL_SENDER 환경변수를 확인하고, 연결 테스트는 워커 파드 안에서 Python 쉘로 수행할 수 있습니다.

from common.client.email.smtp import SMTPClient
client = SMTPClient()
# send_email(...) 직접 호출로 테스트

3.2 이메일 템플릿

템플릿은 언어별로 분리되어 있습니다.

  • 경로: config/templates/email/{ko|en}/<template>.html
  • 엔진: Jinja2

운영에서 내용을 바꿔야 한다면 코드를 수정하고 재배포 해야 합니다. 관리자 화면에서 실시간으로 바꿀 수 있는 기능은 없습니다.

템플릿발송 시점
email_verification회원가입 이메일 인증
new_user신규 사용자 초대
new_company신규 회사 가입 환영
request_reset_password비밀번호 재설정 요청
reset_password_token재설정 링크 메일
reset_password_complete재설정 완료 알림
onboard_complete온보딩 완료
invoice청구서 발송
contract_expiration고객사 계약 만료 알림
tech_support기술 지원 요청

템플릿 목록 소스: config/email.py.

3.3 수신 확인

메일 발송 기록은 invoice_deliveries 컬렉션에 남습니다 (청구서 한정, TTL 365일). 그 외 초대/재설정 메일은 별도 기록이 없으므로 SMTP 서버 로그 또는 수신자의 스팸함을 확인합니다.

3.4 발송 실패 대응

  1. 전체 실패: SMTP_* 자격증명 만료·차단 여부 확인. EMAIL_SENDER 도메인의 SPF/DKIM 이 올바른지 점검.
  2. 일부 수신자만 실패: 대상 메일함 쪽에서 차단. 수신 허용 주소 요청.
  3. 청구서 발송 실패: invoice_deliveries 의 해당 레코드 상태 확인 후, API로 재발송 트리거.

4. 인앱 알림

콘솔 우측 상단의 종 모양 알림은 notification 컬렉션으로 관리됩니다.

시나리오트리거
고객사 계약 만료 D-30 / D-7 / D-Daycontract_expiration_task 스케줄러
만료된 조직 자동 중지auto_suspend_expired_organizations 스케줄러
청구서 생성 완료nhn_invoice_task 완료 시

알림 발송 대상은 해당 회사의 ADMIN 이상 사용자 전원입니다.

// 특정 사용자의 미확인 알림
db.notification.find({
  user_id: '<USER_UID>',
  check_at: null
}).sort({ created_at: -1 });

5. 고객 계정 연동 설정

NHN Cloud 리셀러 계정 연동은 각 회사(MSP)가 직접 입력하는 구조이지만, 시스템 전역 HMAC/Access/Secret Key는 .env 에 미리 주입되어 있어야 합니다(§1.4, §1.5).

사용자 입력 항목은 기능 정리 - 시작하기 §3.1 참조.


6. 체크리스트

운영 인수 직후 한 번씩 확인:

  • APP_ENV=prd 로 동작하고 있는가? (prd-env 시크릿 값)
  • NHN__*, NHN_GOV__* 키가 모두 채워져 있는가? (해당 환경에서 사용 안 하면 비어도 무방)
  • SMTP_* 로 실제 메일이 발송되는가? 본인 이메일로 초대/재설정 테스트.
  • MONGODB_PASSWORD, REDIS_PASSWORD, SMTP_PASSWORD 값이 Kubernetes Secret으로 주입되어 평문 매니페스트에 노출되지 않았는가?
  • 최소 1명의 ROOT 또는 SYSTEM_ADMIN 계정이 있는가?
  • 비상시 DB 접속 자격증명을 운영 담당자가 보유하고 있는가?