역할

PIW(Partner API WAS)는 가전사(삼성 SmartThings, LG ThinQ)에 LH 세대 설비를 열어주는 창구임. 전체 그림은 공개노트/개발/시스템 구성.

  • 가전사 앱 → PIW: /api/eum/devices, /api/eum/user (OAW 발급 JWT로 인증)
  • PIW → APW: /api/proxy-homenet/* (내부 ApiKey). 실제 홈넷 제어는 APW가 전달함
  • APW/MAW → PIW: /api/eum/partner/devices (내부 ApiKey). 기기 상태 변경을 받아 가전사에 전달함
  • PIW → 가전사: 삼성은 공개노트/개발/모듈/SCI 삼성 커넥터(http://localhost:8088/api/sci/partner)로, LG는 ThinQ 이벤트 엔드포인트(.../event/lhc)로 발송함

Spring Security 의존성 없음. 인증은 PiwRequestFilter(가전사용 JWT 검사), IsdRequestFilter(내부 ApiKey 검사) 두 서블릿 필터가 전담함.

기술 스택

항목
프레임워크Spring Boot 2.7.18, Java 8, Gradle, war(piw.war)
DBPostgreSQL smahdb, JPA + hibernate-types(jsonb)
캐시Redis Sentinel(mymaster)
JWTjjwt, HS256, 검증만 함(발급은 OAW)
HTTP 클라이언트WebClient, 응답 타임아웃 10초
포트8087(모든 프로필 공통)
로그/app/logs/was/tomcat-piw/piw.log, piw_error.log(일별 롤링 30일). 모든 라인에 [traceId=%X{traceId}]

API 8개

#메서드경로인증하는 일
1GET/api/eum/devicesJWT세대 장치 목록(승강기 가상 장치 포함, safety·noise 제외)
2GET/api/eum/devices/{dvcId}JWT장치 1건 상태(내부적으로 목록 조회 재사용)
3PATCH/api/eum/devices/{dvcId}JWT장치 제어
4POST/api/eum/devices/elevatorJWT승강기 호출(공통)
5POST/api/eum/devices/elevator/{ptnrId}JWT승강기 호출(삼성 전용, called/standby 이벤트 2회)
6GET/api/eum/userJWT토큰 기반 사용자 정보(DB 조회 없음)
7POST/api/eum/partner/devices내부 ApiKey기기 상태 변경 수신 → 가전사 전달
8GET/api/eum/partner/token/{ptnrId}내부 ApiKeyLG ThinQ Business API 토큰 발급(운영·디버깅용)

핵심 규칙

  • 승강기는 홈넷 기기가 아니라 PIW가 만들어내는 가상 장치임(elevator, 초기 상태 standby).
  • 장치 제어(3번) 성공 후 0.1초 대기했다가 최신 상태를 재조회해 파트너사에 이벤트로 발송함. 코콤이 모든 상태를 안 보내는 문제 해결용(커밋 7afb4b5d).
  • 삼성 전용 승강기 호출(5번)은 1초 뒤 called, 20초 뒤 standby 이벤트를 순서대로 보냄. 실제 승강기 위치와 무관한 시뮬레이션 값임. “앱은 도착이라는데 실제로 안 왔다”는 문의가 이 때문에 생김.
  • 삼성 전송 토큰은 **Redis Partner:{installedAppId}accessToken**에서 읽음(tb_oauth_m.istai가 installedAppId). 이 값 없으면 Bearer null로 나감.

기기 타입 매핑 (LH → 삼성 SseDvcType)

LH type삼성 타입LH 속성변환 규칙
lightHNLIGHTpower값 그대로
light(dimming 있음)HNDIMLIGHTdimmingdimming 속성 있으면 타입을 덮어씀
thermostatHNHEATpower/temperature/setTemperature값 그대로
gasHNGASlockoffclose, 그 외→open
ventilatorHNVENTpower/auto/speedauto=onfan_speed=0, 아니면 speed 값
elevatorHNELEVATORstatus값 그대로
outingHNAWAYsetoffoff, 그 외→on
preventionHNSECURITYsetoffoff, 그 외→on
safety, noise가전사에 노출 안 함(목록에서 제거)

LG는 삼성과 같은 SseDvc 클래스를 재사용함.

외부 연동

대상방향엔드포인트인증
APW송신/api/proxy-homenet/*내부 ApiKey(tb_cstt_m.SMAH_INTERNAL_API_KEY)
삼성(SCI)송신POST http://localhost:8088/api/sci/partnerBearer {Redis accessToken}
LG ThinQ송신POST {api-event.uri}/event/lhcX-Api-Key, X-Message-Id(traceId)
LG ThinQ Business API송신POST {api.uri}/tokenX-Api-Key, X-Api-Secret(토큰 24시간 유효)

데이터

테이블용도
tb_oauth_m가전사 연동 상태. ptnr_id, lh_refsh_tkn, istai(삼성 installedAppId)
tb_hsh_dvc_m세대 장치. iot_dvc_tp_cd
tb_ptnr_hnet_*_cont_l기기 타입별 제어 로그(조명·난방·환기·가스·외출·방범)
tb_ptnr_smah_api_rq_l제어 외 모든 가전사 요청 로그(pk trc_id)
alog.tb_piw_ptnr_api_lPIW → 가전사 호출 로그, ptnr_api_rsp_cd

알려진 이슈

  • IsdRequestFilter의 URL 패턴이 /api/eum/partner/**임. 서블릿 URL 매핑 규격상 /**는 리터럴 정확 매치라 실제 경로(/api/eum/partner/devices)에는 필터가 안 걸릴 가능성 있음. PIW에 Spring Security가 없어 대체 방어선도 없음.
  • LgeEvnApi.sendDvcStt()data를 채우는 코드 없이 빈 배열을 보냄. LG로는 항상 빈 이벤트가 나가는 것으로 추정됨.
  • prod의 LG 토큰 발급 URI 끝에 /token이 있어 baseUri + "/token" 호출 시 /token/token이 될 가능성 있음(설정 오타로 추정).
  • ApwApi/IsdRequestFilter 생성자가 tb_cstt_m 조회 실패 시 NPE로 애플리케이션 기동 자체가 실패함.
  • LG API 키·시크릿이 설정 파일에 평문으로 있음(jasypt ENC[] 미적용).
  • getDevice(장치 1건 조회)가 APW 목록 조회를 재호출함. 장치 하나 보는 데도 홈넷서버 왕복 발생(성능 주의).

확인 필요

확인 필요

  • IsdRequestFilter/api/eum/partner/** 패턴이 실제로 필터를 태우는지, 내부 API가 무인증 노출 상태인지.
  • LgeEvnApi가 빈 data를 보내는 것이 의도인지, LG 연동이 실제 운영 중인지.
  • prod sqi.partner.lge.api.uri/token 중복이 오타인지.
  • 승강기 시뮬레이션 시간(1초/20초)을 실제 신호로 바꿀 계획이 있는지.
  • safety(안전), noise(층간소음) 타입을 가전사에 노출하지 않기로 한 근거.
  • /api/eum/partner/token/{ptnrId}를 실제로 호출하는 주체.

관련

공개노트/개발/시스템 구성 공개노트/개발/모듈/APW 연계 서버 공개노트/개발/모듈/SCI 삼성 커넥터 공개노트/개발/모듈/THA 홈넷 시뮬레이터 공개노트/기능/홈넷 서버 연동 공개노트/기능/홈넷 이벤트 알림 공개노트/기능/승강기 호출