새기능 #5837
완료됨[구축] pms-slack-sync - PMS 이슈를 슬랙 리스트에 자동 동기화하는 모듈
0%
설명
운영 문서¶
설치 / 재배포 / 로그 확인 / 문제 해결은 모두 위키를 참고한다.
이 이슈는 개발 환경과 소스 기준만 기록한다.
개요¶
PMS(Redmine)에 이슈를 등록하거나 수정하면 슬랙 #cs요청-문자온개선-mjon 채널의 PMS 추적기 리스트가 자동으로 따라오는 모듈이다. PMS 등록 후 슬랙 리스트에 같은 내용을 손으로 한 번 더 입력하던 이중 작업을 없앤다.
방향은 PMS → 슬랙 단방향이며 PMS가 기준이다. 슬랙 리스트에서 고친 값은 다음 동기화 때 PMS 값으로 덮어써진다.
소스 기준¶
| 항목 | 값 |
|---|---|
| 저장소 | http://vcs.iten.co.kr/in_project/pms-slack-sync |
| 브랜치 | master |
| 기준 커밋 | bb1d35a |
| 총 커밋 | 61 |
| 서버 배포 위치 | /home/docker/rm_9011_compose_2022_0603/pms-slack-sync |
개발 / 실행 환경¶
| 항목 | 버전 · 값 |
|---|---|
| 언어 | Python 3.12 |
| 베이스 이미지 | python:3.12-slim |
| 런타임 의존성 | httpx 0.27.2 (이것 하나뿐) |
| 테스트 도구 | pytest 8.3.3, respx 0.21.1 |
| 저장소(DB) | SQLite (파이썬 표준 라이브러리, ORM 미사용) |
| 컨테이너 | Docker Compose v1 (docker-compose, 하이픈) |
| 실행 사용자 | UID 10001 (비-root) |
| 노출 포트 | 없음 |
| 배포 서버 | 192.168.0.176 |
웹 프레임워크를 넣지 않았다. 1단계는 폴링 전용이라 들어오는 요청을 받을 일이 없다.
기존 redmine 도커 스택에 컨테이너 하나를 추가한 형태이며, 같은 네트워크(backend_app)를 쓰기 때문에 PMS를 외부 주소가 아니라 http://redmine:80 으로 직접 호출한다.
연동 기준¶
Redmine REST API¶
| 항목 | 값 |
|---|---|
| 대상 프로젝트 | 문자온(11) + 하위 48, 33, 49, 44, 47 |
| 조회 주기 | 60초 |
| 조회 조건 | updated_on >= 마지막 조회 시각, status_id=* (종료 이슈 포함) |
| 필요 권한 | 관리자 (사용자 목록 조회에 필요) |
Slack Lists API¶
2025년 9월 공개된 API를 사용한다.
| 항목 | 값 |
|---|---|
| 사용 메서드 | slackLists.items.list / .info / .create / .update, users.lookupByEmail |
| 대상 리스트 | PMS 추적기 (F088BD47D1R) |
| 필요 스코프 | lists:read, lists:write, users:read, users:read.email |
| 슬랙 플랜 | Lists 기능은 유료 플랜에서만 동작 |
동기화 항목¶
동기화하는 필드¶
| 리스트 컬럼 | PMS 항목 | 비고 |
|---|---|---|
| 업무 | 제목 | |
| 상태 | 상태 | 이름이 같은 옵션으로 매칭 |
| 우선순위 | 우선순위 | 낮음=★ ~ 즉시=★★★★★ |
| 설명 | 이슈 링크 | |
| 담당자 | 담당자 | 이메일로 슬랙 계정 매칭 |
동기화하지 않는 필드¶
수정자, 등록시간, 마지막 편집 시간은 슬랙이 자동으로 채우는 타입이라 API로 값을 넣을 수 없다. 동기화된 항목의 수정자는 봇으로 표시되고, 등록시간은 리스트에 행이 생긴 시각이 된다. PMS의 실제 값은 설명 링크로 확인한다.
상태 매핑¶
PMS 상태와 슬랙 옵션을 이름 그대로 맞췄다. 아래 4개는 사용 빈도가 낮아 의도적으로 슬랙 리스트에 만들지 않았다. 해당 상태의 이슈는 상태 칸이 빈 채로 동기화된다.
- 일시 중단 / 거절 / 원격지원 진행 / 원격지원 완료
구조¶
app/
config.py 환경변수 로딩·검증
urls.py 이슈 URL 생성·파싱 (호스트 검증)
redmine.py Redmine REST 클라이언트
slack.py Slack Lists API 클라이언트 (재시도·레이트리밋)
schema.py 리스트 컬럼 스키마 조회
transform.py 이슈 → 셀 값 변환 (순수 함수)
users.py Redmine ↔ 슬랙 사용자 매칭
mapping.py 이슈번호 ↔ 리스트 행 ID 저장소
sync.py 생성/수정 판단, 실패 격리
poller.py 주기 폴링, 워터마크 관리
main.py 조립 및 기동
scripts/
bootstrap.py 기존 리스트 항목을 이슈에 연결 (최초 1회 / 복구용)
dump_schema.py 리스트 스키마 덤프 (조사용, 읽기 전용)
테스트¶
- 단위·통합 테스트 198개 통과
- 외부 호출은 respx로 대체해 네트워크 없이 실행된다. 자격증명 없이
python -m pytest로 바로 돌릴 수 있다
현재 상태¶
- 기존 리스트 항목 299건 매핑 연결 완료
DRY_RUN=true로 기동해 검증 중 (이 상태에서는 슬랙에 아무것도 쓰지 않고 무엇을 쓸 예정인지만 로그로 남긴다)- 검증 후
DRY_RUN=false로 전환 예정
남은 작업 (선택)¶
2단계 — 웹훅 도입. 현재는 폴링이라 최대 1분 지연이 있다. 실시간이 필요하면 Redmine에 redmine_webhook 플러그인을 설치하면 된다. 다만 현재 redmine 컨테이너는 plugins 디렉토리가 볼륨 마운트되어 있지 않아 이미지 재빌드 또는 볼륨 추가 + 마이그레이션이 필요하고, 운영 중단을 수반한다. 1분 지연이 문제되지 않으면 하지 않아도 된다.