giip
SES 안건 등록
10분 읽기

이슈 관리 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/giipIssueCommentsrun.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 사용자 관리 화면)동일
로그인 세션 AKtUserLogin.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/giipIssuesisn을 생략(또는 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 }
  • 코멘트 등록: POST /api/giipIssueComments
    • Body: { "isn":7890, "content":"처리 완료", "author":"api-tester", "issuetype":"comment" }{ "success":true }
    • 코멘트 조회: GET /api/giipIssueComments?isn=7890{ "comments":[...] }

전달 방식 요약 (실측, 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 예시성공 응답
목록 조회statusGiipIssueList READYdata(이슈 배열)
상세 조회isnGiipIssueGet 7890data[0](이슈 1건)
원격 실행isnGiipIssueDispatch 7890data[0].RstVal = 200

🚫 GiipIssuePut을 Sk2로 상태변경에 쓰지 마세요 (데이터 파괴 위험). Sk2 GiipIssuePut은 내부 SP pApiGiipIssuePutbySK(@sk, @isn, @title, @content, @status, @csn)에 매핑되는 전체 덮어쓰기(full overwrite) 입니다(coalesce 없음). GiipIssuePut 7890 DONE로 부르면 두 번째 값 DONE@title로, 세 번째(jsondata)가 @content로 들어가 제목·본문이 파괴됩니다. 응답은 RstVal:200(가짜 성공)으로 나오지만 실제로는 오손된 것입니다(실측: 2026-07-09, task 20260708183049). 상태만 안전하게 바꾸려면 전용 REST PUT /api/giipIssues {isn, status}를 쓰세요. 내부 pApiGiipIssuePutbyAKISNULL(@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이 되는 구체적 원인:

  1. 키 값에 오타가 있거나 존재하지 않음(어떤 AK/SK 테이블에도 매칭되지 않음)
  2. 유효했던 프로젝트 SK가 재발급/회전으로 비활성화됨(SKStatus=0) — giip #1265에서 실측
  3. 로그인 세션 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].RstVal200이 아님(Sk2)Stored Procedure 실행 오류 또는 잘못된 isnProc_MSG 확인 후 API 결과 코드 가이드 참조 (성공은 0이 아니라 200)
긴 에러 메시지·특수문자 전송 시 값 유실JSON 값 이스케이프 처리 누락giipApiSk3jsondata 치환 기능으로 안전하게 전송

버전: 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 보존) 추가.


관련 문서: