이슈 관리 API 레퍼런스
GIIP 플랫폼에서 발생하는 이슈 및 에러 로그를 프로그래밍 방식으로 조회하고 상태를 업데이트하는 API를 안내합니다.
📋 개요
이 API 모듈은 실시간 이슈 관리 솔루션인 [이슈 관리] 메뉴의 데이터를 외부 시스템과 동기화하거나, 장애 대응 파이프라인(ITSM 등)과 연동하기 위해 사용됩니다.
🔐 인증 (Authentication)
이 API가 받아들이는 키의 종류, 키를 전달하는 방식, 그리고 오류 원인을 실제 서버 코드
(giipfaw/giipIssues/run.ps1, giipfaw/giipIssueComments/run.ps1, 대응 pApiGiipIssue*byAK SP군)와
라이브 API 실측(giipfaw, 2026-08-20) 양쪽으로 확인한 내용을 기준으로 설명합니다. 공통 AK/SK 용어 정의는
API 레퍼런스 개요 → 인증 을 참조하세요.
키 전달 방식 — 헤더만 지원 (실측)
| 전달 방식 | 지원 여부 |
|---|---|
x-api-key: <key> 헤더 | ✅ 지원(권장) |
Authorization: Bearer <key> 헤더 | ✅ 지원(대체 가능) |
JSON 바디 { "token": "<key>" } | ❌ 미지원(서버 코드가 읽지 않음. 전달해도 401 Auth required) |
쿼리 문자열 ?token=<key> | ❌ 미지원/지원중단(위와 동일. 게다가 키를 쿼리 문자열에 싣는 방식 자체가 서버 로그·브라우저 기록·프록시 로그에 키를 남기므로 보안상 사용해서는 안 됩니다) |
이 문서의 이전 버전에는 "Body/Query로도 전달 가능"이라는 내용이 있었지만,
giipIssues/giipIssueComments의run.ps1은$Request.Headers["x-api-key"]→ 실패 시Authorization헤더, 이 2가지만 읽으며 실제로는 동작하지 않습니다(2026-08-20, 코드 확인 + 라이브 실측으로 삭제 확정). 키는 반드시 헤더로 전달하세요.
키의 종류(3가지, 권한 범위가 다름)
| 종류 | 실체 | 발급 단위 | 권한 범위 | 발급/확인 | 검증 방법 |
|---|---|---|---|---|---|
| 프로젝트 SK(권장·에이전트용) | tSecretKey.SKey | 프로젝트(csn)당 1개 | 그 csn만. 호출자 개인은 특정되지 않는 공유 키(코멘트 등록 시 author는 클라이언트가 지정한 값이 그대로 사용됨 — 실측 확인) | /svclist(서비스 목록) 화면에서 발급/재발급 | 아래 "인증 확인 절차" 참조 |
사용자 고정 키(tCorpUser.uSecretKey) | 동일 | 사용자(usn)당 1개 | 해당 사용자 본인이 속한 csn(tUserPerCorp/tCorpUserRel). 관리자 권한(uLevel≥99) 사용자라면 전체 csn | 관리자가 발급(giipv3 사용자 관리 화면) | 동일 |
| 로그인 세션 AK | tUserLogin.AccToken | 로그인할 때마다 신규 발급, 24시간 후 만료 | 로그인한 사용자 본인과 동일한 권한 | 브라우저 로그인 시 자동 발급(sessionStorage) | 동일 |
세 종류 모두 같은
x-api-key헤더로 전달하면 서버가 자동으로 판별합니다(lwGetUSNbyat→ 실패 시lwGetUSNbysk로 폴백하는 체인). 응답만으로는 호출자가 어떤 종류를 썼는지 구분할 수 없습니다(성공/실패 동작은 동일)만, 권한 범위(어느 csn에 접근 가능한지)는 종류마다 다릅니다.이번 세션의 실측은 주로 프로젝트 SK(
giip-accounts.json에 등록된 실운영 SK)로 진행했습니다. 사용자 고정 키·로그인 세션 AK는 이 세션에서 브라우저 로그인으로 신규 발급할 수 없어 소스 코드 분석으로만 확인했습니다(lwGetUSNbyat.sql의 3단계 폴백 로직에서 존재를 확인함. 라이브 신규 발급·실측은 수행하지 않음 — 솔직히 밝힙니다).
SK의 조건(전부 실측+코드 확인)
- 발급 위치: giipv3의
/svclist(서비스 목록) 화면. 프로젝트(csn)당 1개. - 활성화 확인:
tSecretKey.SKStatus = 1일 때만 유효합니다. 재발급/회전하면 이전 SK는 즉시SKStatus = 0이 되고, 이후401 Invalid session을 반환합니다(giip #1265에서 실측: 활성 SK→200, 비활성화된 SK→401둘 다 확인). - 연결 csn:
tSecretKey.CSn에 1:1로 고정됩니다. (관리자 권한이 아닌 한) 다른 csn에는 접근할 수 없습니다 (아래 "csn 권한 불일치 시 동작" 참조). - 필요 권한: SK 자체가 해당 csn 접근 권한을 나타내므로 추가 권한 설정이 필요 없습니다.
- 만료 여부: 시간 경과로 자동 만료되지 않습니다(로그인 세션 AK와 달리 24시간 제한 없음). 비활성화되는 경우는 재발급/회전 시뿐입니다.
- 허용 엔드포인트:
GET/POST/PUT /api/giipIssues,GET/POST /api/giipIssueComments전부에서 사용 가능(실측 확인). 단giipApiSk2경유GiipIssuePut커맨드는 전체 덮어쓰기 SP라 별개이므로 쓰기에는 쓰지 마세요(아래 "방법 2" 참조). - 키 검증용 읽기 API: 쓰기(POST/PUT) API를 호출하기 전, 부작용 없는
GET /api/giipIssues로 먼저 키가 유효한지 안전하게 확인할 수 있습니다(아래 "인증 확인 절차" 참조). - 재발급 방법:
/svclist화면에서 대상 csn의 SK를 재생성합니다(이전 SK는 즉시 무효화되므로, 이 키에 의존하는 자동화가 있다면 함께 갱신하세요).
인증 확인 절차(쓰기 전 안전하게 검증)
쓰기 API(POST/PUT)를 호출하기 전에, 부작용 없는 GET으로 키의 유효성을 먼저 확인하세요.
curl -s -o /dev/null -w "%{http_code}\n" \
"https://giipfaw.azurewebsites.net/api/giipIssues?csn=47" \
-H "x-api-key: ${GIIP_API_KEY}"
- 성공 시(
200):{"issues":[...]}— 이 키를 그대로 생성/상태변경 API에 사용할 수 있습니다. - 실패 시(
401):{"error":"Auth required"}(헤더 자체가 없거나 읽히지 않음) 또는{"error":"Invalid session"}(키는 있지만 무효 — 자세한 내용은 아래 "401 Invalid session 원인 분류" 참조). 이 경우 생성 API를 실행하지 마세요.
🚀 주요 API 엔드포인트 (실제 구현 기준)
이슈 API는 두 경로 모두 정상 동작합니다. 신규 등록·코멘트 등 CRUD는 전용 REST 엔드포인트(giipv3 어드민 UI가 실제 사용하는 경로)를, SP 직접 호출은 giipApiSk2 래퍼를 사용합니다.
1. 이슈 전용 엔드포인트 (REST API)
가장 직관적인 호출 방식이며, giipv3 어드민 UI가 실제 사용하는 경로입니다. 인증은 x-api-key 헤더, Content-Type은 application/json. 키는 환경변수로 전달하고 하드코딩하지 마세요.
- 신규 등록:
POST /api/giipIssues—isn을 생략(또는 0)하면 신규 이슈가 INSERT되고 새isn을 반환합니다.
curl -s -X POST "https://giipfaw.azurewebsites.net/api/giipIssues" \
-H "x-api-key: ${GIIP_API_KEY}" -H "Content-Type: application/json" \
-d '{ "title":"제목(필수)", "content":"본문", "status":"PENDING",
"csn":47, "target_lssn":null, "agent_workflow":null }'
- 성공:
{ "isn":577, "message":"Issue created", "success":true }(실측: 2026-08-20, isn 1282로 확인 후 삭제) - 목록 조회:
GET /api/giipIssues?status=READY&csn=47(csn 지정 권장 — 생략하면 키 권한 범위 내 전체 csn 반환) - 상세 조회:
GET /api/giipIssues?isn=7890 - 상태 업데이트/완료:
PUT /api/giipIssues- Body:
{ "isn":7890, "status":"DONE" }→{ "success":true }
- Body:
- 코멘트 등록:
POST /api/giipIssueComments- Body:
{ "isn":7890, "content":"처리 완료", "author":"api-tester", "issuetype":"comment" }→{ "success":true } - 코멘트 조회:
GET /api/giipIssueComments?isn=7890→{ "comments":[...] }
- Body:
전달 방식 요약 (실측, 2026-08-20)
| 엔드포인트 | x-api-key 헤더 | Authorization: Bearer 헤더 | JSON 바디 token | 쿼리 ?token= |
|---|---|---|---|---|
GET /api/giipIssues | ✅ 실측 200 | ✅ 실측 200 | ❌ 미지원(401) | ❌ 실측 401 |
POST /api/giipIssues(생성) | ✅ 실측 200 | 코드 동일(미실측·고신뢰) | ❌ 실측 401 | ❌ 실측 401 |
PUT /api/giipIssues(상태변경) | ✅ 실측 200 | 코드 동일(미실측·고신뢰) | ❌ 실측 401 | 코드 동일(미실측·고신뢰) |
POST /api/giipIssueComments | ✅ 실측 200 | 코드 동일(미실측·고신뢰) | ❌ 실측 401 | ❌ 실측 401 |
GET /api/giipIssueComments | ✅ 실측 200(※아래 버그 주의) | 코드 동일(미실측·고신뢰) | ❌ 미지원 | ❌ 미지원(미실측·고신뢰) |
"코드 동일(미실측·고신뢰)"은 4개 엔드포인트 모두의 인증 추출 코드(
$Request.Headers["x-api-key"]→ 실패 시Authorization헤더, 이 2가지뿐)가giipfaw/giipIssues/run.ps1·giipIssueComments/run.ps1에서 완전히 동일함을 소스로 확인한 뒤의 추정입니다(전체 20칸을 매번 실행하지는 않았고, 주요 칸을 실측하고 나머지는 코드 리뷰로 대체했습니다).
⚠️ 알려진 버그:
GET /api/giipIssueComments는 무효한 키로 호출해도 HTTP 401이 아니라 HTTP 200을 반환하며{"comments":[{"RstVal":401,"Proc_MSG":"Invalid session"}]}을 돌려줍니다(실측 확인, 2026-08-20).GET /api/giipIssues는 동일 상황에서 정상적으로401을 반환하므로 두 엔드포인트의 동작이 다릅니다. 원인은giipIssueComments/run.ps1의 GET 핸들러가giipIssues/run.ps1의 GET 핸들러와 달리 결과 셋에RstVal컬럼이 있는지 확인하지 않기 때문입니다. 코멘트 조회 API를 쓰는 클라이언트는 HTTP 상태 코드뿐 아니라 응답 본문의 형태도 확인해야 합니다. 이 버그는 후속 이슈로 등록했습니다(하단 참조).
csn 권한 불일치 시 동작 (401 아님)
키는 유효하지만 대상 csn에 접근 권한이 없는 경우, 401이 되지 않습니다(실측 확인, 2026-08-20).
| 호출 | 실측 결과 |
|---|---|
GET /api/giipIssues?csn=<권한없음> | 200 {"issues":[]}(빈 배열. 오류 아님) |
POST /api/giipIssues에서 csn에 권한 없는 값을 지정 | 200으로 성공하지만, 지정한 csn은 무시되고 키 자신의 홈 csn으로 조용히 클램프(clamp)됨(실측: csn70335의 SK로 csn:47 지정 → 실제로는 cSn:70335로 생성됨) |
특정 isn의 GET/PUT/코멘트 등록에서 대상 이슈의 csn에 권한이 없음 | 404 {"error":"Issue not found or no permission"} |
2. 범용 API 래퍼 (giipApiSk2)
GIIP의 Stored Procedure를 직접 호출하는 sk 기반 방식이며, 조회(List/Get)와 에이전트 액션(Dispatch/ReadinessCheck) 에 사용합니다. 생성·상태변경·코멘트 등 쓰기(write)는 이 경로로 하지 말고 위 §1 전용 REST 엔드포인트를 사용하세요(이유는 아래 🚫 참고).
- URL:
POST /api/giipApiSk2 - Content-Type:
application/x-www-form-urlencoded - 필드:
token:[Your_SK]— SK는 반드시 이 필드로만 전달text:[Command] [파라미터...]jsondata: 값 매핑용 JSON(선택). 예:{}또는{"lssn":101}
⚠️ text 작성 규칙(엄수):
text에는 해당 SP가 받는 정확한 파라미터만 순서대로 나열합니다. 인증(@sk)과 jsondata는 엔진(run.ps1)이 자동 처리하므로 절대 나열하지 마세요. 파라미터를 과다 나열하면 SP 인자 개수를 초과해 has too many arguments specified 오류가 납니다.
| 명령(안전) | SP 파라미터(=text에 나열) | text 예시 | 성공 응답 |
|---|---|---|---|
| 목록 조회 | status | GiipIssueList READY | data(이슈 배열) |
| 상세 조회 | isn | GiipIssueGet 7890 | data[0](이슈 1건) |
| 원격 실행 | isn | GiipIssueDispatch 7890 | data[0].RstVal = 200 |
🚫
GiipIssuePut을 Sk2로 상태변경에 쓰지 마세요 (데이터 파괴 위험). Sk2GiipIssuePut은 내부 SPpApiGiipIssuePutbySK(@sk, @isn, @title, @content, @status, @csn)에 매핑되는 전체 덮어쓰기(full overwrite) 입니다(coalesce 없음).GiipIssuePut 7890 DONE로 부르면 두 번째 값DONE이 @title로, 세 번째(jsondata)가 @content로 들어가 제목·본문이 파괴됩니다. 응답은RstVal:200(가짜 성공)으로 나오지만 실제로는 오손된 것입니다(실측: 2026-07-09, task 20260708183049). 상태만 안전하게 바꾸려면 전용 RESTPUT /api/giipIssues {isn, status}를 쓰세요. 내부pApiGiipIssuePutbyAK가ISNULL(@title, title)로 기존 제목·본문을 보존합니다(=giipv3 프론트가 실제 쓰는 경로).
🔍 응답 데이터 예시
giipApiSk2 (SP 래퍼) 응답 — 조회는 data 배열에 레코드, SP 액션(Dispatch 등)은 성공 시 RstVal = 200:
{ "data": [ { "RstVal": 200, "Proc_MSG": "Dispatched", "isn": 7890 } ] }
조회 명령(GiipIssueList/GiipIssueGet)은 data 배열에 이슈 레코드가 담깁니다.
전용 REST 엔드포인트 응답 — 성공 시 success: true (응답 본문에 RstVal 없음):
{ "isn": 7890, "message": "Issue updated", "success": true }
ℹ️ 성공 판정 주의: SP 성공 코드는
RstVal = 200이며0이 아닙니다. 실패 시 400/401/403/404를 반환합니다(K-Layer CLAIM-006). 전용 엔드포인트는RstVal대신success불리언을 반환합니다.
💡 활용 사례
- 슬랙 알림 봇: API를 주기적으로 호출하여 신규
Critical이슈 발생 시 즉시 개발팀 채널에 메시지를 보냅니다. - 자동 복구 스크립트: 특정 유형의 이슈(예: 프로세스 다운) 탐지 시 API로 이슈를 확인하고, 원격 명령 API(
api-system)를 호출하여 해당 서비스를 재시작합니다.
🛡️ Sk3(고성능 로깅) 활용
장애 대응 시스템이나 외부 티켓팅 도구와의 연동 시, 업데이트 무결성 보장과 상세한 호출 이력 추적을 위해 giipApiSk3 엔드포인트를 권장합니다.
- 엔드포인트:
https://giipfaw.azurewebsites.net/api/giipApiSk3 - 장점: 이슈 상태 업데이트(Done 처리 등) 실패 시 호출자의 상세 환경 정보(IP, UA)와 StackTrace를 즉시
tErrorLogs에 기록하여 장애 대응 프로세스의 신뢰성을 높여줍니다. - 활용 팁:
jsondata치환 기능을 사용하여 이슈 내의 특정 변수값이나 긴 에러 메시지를 파라미터 유실 없이 안전하게 전송할 수 있습니다.
401 "Invalid session" 원인 분류
401에는 서로 다른 의미의 메시지 2가지가 있습니다(실측 확인, 2026-08-20).
| 응답 | 원인 |
|---|---|
{"error":"Auth required"} | 키가 서버까지 도달하지 못함 — x-api-key/Authorization 헤더가 없거나, 지원되지 않는 전달 방식(JSON 바디 token, 쿼리 ?token=, 잘못된 헤더 이름 등)을 사용함 |
{"error":"Invalid session"} | 키는 도달했지만 무효 — 아래 원인 중 하나 |
Invalid session이 되는 구체적 원인:
- 키 값에 오타가 있거나 존재하지 않음(어떤 AK/SK 테이블에도 매칭되지 않음)
- 유효했던 프로젝트 SK가 재발급/회전으로 비활성화됨(
SKStatus=0) — giip #1265에서 실측 - 로그인 세션 AK가 24시간 유효기간을 초과함(
tUserLogin.AccToken, 코드 확인만·미실측)
해당하지 않는 경우(오해하기 쉬운 지점):
- AK 전용 엔드포인트에 SK를 쓴 경우/그 반대 → 해당 없음.
giipIssues/giipIssueComments는 AK·SK 어느 쪽으로도 동작하도록 구현돼 있으며, 종류별 전용 엔드포인트 구분이 없습니다(실측+코드 확인). - 대상 csn에 권한이 없는 경우 →
401이 아니라 위 "csn 권한 불일치 시 동작"대로200(빈 배열)/200(조용한 클램프)/404가 됩니다.
문제 해결
| 증상 | 원인 | 해결 |
|---|---|---|
{"error":"Auth required"} (401) | 헤더에 키가 없거나, 바디/쿼리 등 미지원 방식으로 전달함 | x-api-key: <key> 헤더(또는 Authorization: Bearer <key>)로 전달. 바디 token·쿼리 ?token=은 사용 불가 |
{"error":"Invalid session"} (401) | 키 오타, 비활성화(재발급/회전), 세션 만료 중 하나 | 위 "인증 확인 절차"의 GET으로 키를 재검증. /svclist에서 대상 csn의 현재 유효한 SK 확인 |
프로젝트 이슈가 [](빈 배열)로 반환됨 | 키는 유효하지만 대상 csn에 권한이 없음(401이 되지 않음) | 대상 csn용 키를 사용하거나 관리자에게 권한 부여를 요청 |
POST에서 csn을 지정했는데 다른 csn에 생성됨 | 권한 없는 csn을 지정하면 키의 홈 csn으로 조용히 클램프됨(실측: 위 참조) | 생성 후 GET /api/giipIssues?isn=<isn>으로 실제 cSn을 확인. 다른 csn 생성이 필요하면 관리자에게 tUserPerCorp 권한 부여 요청 |
GET /api/giipIssueComments가 HTTP 200인데 내용이 에러 | 알려진 버그(위 "전달 방식 요약" 참조) — 무효한 키에도 200을 반환하고 comments 배열에 {"RstVal":401,...}이 들어감 | HTTP 상태 코드뿐 아니라 comments[0].RstVal이 정상 데이터가 아닌 에러 형태인지 확인 |
| 명령이 실행되지 않거나 빈 결과 반환(Sk2) | text 파라미터 형식 오류(명령·파라미터 구분 누락) | GiipIssueList READY처럼 [명령] [파라미터] 형식 준수 |
... has too many arguments specified 오류(Sk2) | text에 SP 파라미터를 과다 나열 | SP의 정확한 파라미터만 나열. @sk·jsondata는 엔진이 자동 추가하므로 나열 금지 |
상태변경 했더니 제목·본문이 DONE/{} 등으로 파괴됨(Sk2) | Sk2 GiipIssuePut(전체 덮어쓰기 SP)로 상태변경 시도 | 상태변경은 전용 REST PUT /api/giipIssues {isn,status} 사용(§1). Sk2 GiipIssuePut은 write에 쓰지 말 것 |
응답 data[0].RstVal이 200이 아님(Sk2) | Stored Procedure 실행 오류 또는 잘못된 isn | Proc_MSG 확인 후 API 결과 코드 가이드 참조 (성공은 0이 아니라 200) |
| 긴 에러 메시지·특수문자 전송 시 값 유실 | JSON 값 이스케이프 처리 누락 | giipApiSk3의 jsondata 치환 기능으로 안전하게 전송 |
버전: 1.5
최종 업데이트: 2026-08-20(테스트 대상 API: giipfaw 프로덕션, pApiGiipIssue*byAK SP군 — 위에 명시한 각 실측은 이 날짜에 라이브 API로 수행)
소스 파일: giipv3/public/help/giip-issue-api.ko.md
v1.5 변경 이력 (2026-08-20, giip #1280): 인증 관련 내용을 대폭 보강. ① JSON 바디
token·쿼리?token=이giipIssues/giipIssueComments에서 실제로는 동작하지 않음을 코드 확인+라이브 실측(4개 엔드포인트)으로 확정하고 잘못된 서술(x-giip-sk헤더·?token=지원 언급 포함)을 삭제(쿼리 문자열 방식은 보안상으로도 지원 중단 명시). ② SK/사용자 고정 키/로그인 세션 AK 3종류를 발급 테이블 기준으로 정리 (tSecretKey/tCorpUser.uSecretKey/tUserLogin.AccToken). ③ 쓰기 전 키를 안전하게 검증하는 절차 추가. ④401의 원인을 "Auth required(키 미도달)"와 "Invalid session(키 무효)"로 분류하고, csn 권한 불일치는 401이 되지 않음(빈 배열/404/조용한 클램프)을 실측으로 명시. ⑤GET /api/giipIssueComments가 무효한 키에도 HTTP 200을 반환하는 알려진 버그를 발견·기록(후속 이슈 등록). ⑥ 생성 예제를 하드코딩된 키에서 환경변수(${GIIP_API_KEY})로 변경.
v1.4 변경 이력 (2026-08-20, giip #1265): SK로
POST /api/giipIssues호출 시401 Invalid session을 받는다는 제보를 실측 검증. 라이브 Azure SQL에 배포된pApiGiipIssuePutbyAK정의는 레포 소스와 완전히 동일하며 SK 폴백 인증(lwGetUSNbyat실패 시lwGetUSNbysk)이 이미 정상 배포돼 있음을 확인(배포 갭 아님). 실제로 활성 SK로 호출하면200 Issue created, 비활성화(재발급/회전)된 옛 SK로 호출하면401 Invalid session이 재현됨(2026-08-20 실측: giipfaw 라이브 API에 두 케이스 모두 직접 호출해 확인). 즉 코드 결함이 아니라 문서가 "어떤 SK가 유효한지"를 설명하지 않은 것이 원인 — §인증 및 문제 해결 표에 SK 활성 상태 요건과 재확인 경로(/svclist)를 추가.v1.3 변경 이력 (2026-07-09, task 20260708183049): 실제 프로덕션 API(giipv3 프론트·giipApiSk2·SP 소스)와 대조하여 정합화. ① Sk2 쓰기 경로 위험 경고 추가: Sk2
GiipIssuePut은 내부pApiGiipIssuePutbySK(@sk,@isn,@title,@content,@status,@csn)전체 덮어쓰기 SP라GiipIssuePut 7890 DONE이 제목·본문을 파괴함(응답은 가짜RstVal:200). 실측 확인(577·578 오손 후 복구). 생성·상태변경·코멘트는 전용 REST 엔드포인트로, Sk2는 조회·액션 전용으로 안내. (과거 보고서의 "too many arguments 비호환" 진단은 부분적 관찰이었고, 실제로는 SP가 full-overwrite여서 위험한 것.) ② 성공 코드는RstVal = 200(0 아님) — 응답 예시·문제 해결 표 정정. ③ 이슈 신규 등록 절차(POST /api/giipIssues,isn생략 시 신규) 및 안전한 상태변경(PUT /api/giipIssues {isn,status},ISNULL보존) 추가.
관련 문서: