역할

LMC(LH Manager Web Client)는 LH 본사·지역본부·거점관리소·단지 관리사무소 직원이 쓰는 스마트홈 관리자 웹임. 전체 그림은 공개노트/개발/시스템 구성.

  • 관리자가 브라우저로 접속해 단지·세대·단말기·관리비·공지사항·전자투표를 관리함
  • 백엔드(lhmngr.war, Spring Boot)는 PostgreSQL smahdb를 직접 읽고 씀
  • 홈넷 연동·SMS·푸시가 필요하면 내부 API 서버(SMAH 내부 WAS, http://localhost:8084, 상수 SMAH_INTERNAL_API_WAS_ADR) 를 호출함
  • 입주민 앱과 같은 DB를 공유함. LMC에서 전입/전출을 승인하면 앱 사용자에게 푸시가 나가고 홈넷사에도 이벤트가 전달됨
  • 운영 주소 https://homez.lh.or.kr, 개발 주소 https://homezdev.lh.or.kr

프로젝트는 LH 임대주택 스마트홈 플랫폼 구축용역(2022-09-21 ~ 2023-09-20), 고객사 한국토지주택공사 공공주택전기처임. 근거 LMC/README-LMC.md:1.

결과 코드·상태 코드·화면 안내 문구 원문은 공개노트/개발/모듈/LMC 결과 코드와 안내 문구에 모아 둠. 화면별로 무엇이 저장되고 무엇이 안 되는지(기능 상세·외부 연동·배치·데이터·알려진 이슈)는 공개노트/개발/모듈/LMC 기능 상세에 있음.

기술 스택

항목
백엔드Spring Boot 2.7.0, Java 8, Gradle, war(lhmngr.war), 패키지 루트 kr.or.lh.smah, 메인 SmahApplication
포트·경로8085, 컨텍스트 경로 없음, API 접두사 /api/v1/...
영속 계층JPA + QueryDSL 5.0.0 + MyBatis 2.2.2 병행, log4jdbc, ddl-auto=none
그 외 라이브러리Spring Security, Jasypt(PBEWithMD5AndDES), jjwt 0.9.1, Apache POI(엑셀), Tika 1.28.5(파일 타입 판별)
계층 구조Controller → Service → Mapper(MyBatis XML) / Repository(JPA) → PostgreSQL
백엔드 패키지api/(인증·메뉴·코드·역할·토큰·파일다운로드), lhmngr/(업무 도메인), config/, comn/, aop/, utils/
API 규모컨트롤러 28개, 매핑 약 161개
프론트(운영본)LMC/frontend, Vue 2.6.14 SPA(vue-cli), package.json name smah-project version 6.4.0
프론트 라이브러리@vue/composition-api, Vuex 3, Vue Router 3, Bootstrap-Vue 2.21, axios 0.21, ECharts 4.8 / ApexCharts 3, html2pdf.js / jspdf, vee-validate 3, @casl/ability, xlsx / vue-xlsx
프론트(미사용)LMC/frontend-vue3 — Vue 3.2 + Vite + Vuetify 3(Vuexy 8.0.0 템플릿)
커넥션 풀Hikari max 30, min-idle 5, connection-timeout 30000
업로드파일·요청 각 128MB. 경로 /smahshare/resident, /smahshare/fil, /smahshare/ann, /smahshare/svr_cert, /smahshare/svr_cert/ca_cert
엑셀 다운로드최대 10000건(excel.download.limit-enabled=true)
마스킹 대상 설정값전화번호 ARIA 암호화 키, jasypt 암호화 키 → 설정 파일에 [REDACTED]

frontend-vue3는 2025-03-07 커밋 ecb1437b(vue3 기반 베이스 코드 추가) 한 건이 전부임. 페이지가 index.vue / login.vue / second-page.vue / [...all].vue 4개뿐이고 메뉴도 “Home / Second page”라 구매 템플릿 원본 그대로임. 반면 frontend는 2026-09-14까지 계속 커밋됨. 현재 운영 화면은 Vue 2쪽이고 vue3는 마이그레이션 착수만 한 상태임.

프로필

application.properties가 프로필별 폴더로 분리돼 있음. 프로필을 지정하지 않으면 기동되지 않음.

프로필리소스 폴더CORS 허용APW 호출 대상
localsrc/main/resources-local/localhost:3000-
devsrc/main/resources-dev/https://homezdev.lh.or.krhttp://[IP]:8094
prodsrc/main/resources-prod/https://homez.lh.or.krhttps://homez-api.lh.or.kr:8094
  • 메뉴 조회용 프로그램 구분 코드 server.config.pgmDsCd=LHW
  • 근거 LMC/backend/src/main/resources-prod/application.properties:1-80

빌드·배포

빌드 절차는 공개노트/개발/배포와 빌드에도 정리해 둠.

명령하는 일
./gradlew clean build전체 빌드
./gradlew bootWarlhmngr.war만 생성 (build.gradle:16-21)
./gradlew bootRun --args='--spring.profiles.active=prod'로컬 기동
npm run serve프론트 로컬 개발 서버 port 9091 (--mode local)
npm run build / npm run build-dev프론트 운영 빌드 / 개발 빌드
npm run lint / npm run tESLint / Jest
  • 프론트는 백엔드와 별도 빌드
  • 백엔드 WebConfigclasspath:/static/에서 파일을 못 찾으면 /static/index.html로 포워딩해 SPA 라우팅을 살림(config/web/WebConfig.java:30-44). 즉 프론트 산출물을 백엔드 static에 넣어 war 하나로 배포하는 구성도 가능함. 현재 vue.config.jsoutputDir는 주석 처리돼 있음
  • 운영 빌드는 파일명에 contenthash를 붙임(vue.config.js:31-33). 배포 직후 기존 탭에서 ChunkLoadError가 나면 라우터가 한 번만 자동 새로고침함(10초 내 반복은 중단, router/index.js:202-228). public/force-reload.js도 있음

프론트 환경 파일

파일VUE_APP_API_URLMNGEXP_FORM_FILE_SEQRFE_FORM_FILE_SEQ
.env.productionhttps://homez.lh.or.kr/api/v15556
.env.developmenthttps://homezdev.lh.or.kr/api/v1160161

*_FORM_FILE_SEQ관리비·임대료 엑셀 양식 파일의 첨부파일 일련번호(ahflSn) 임. “양식 다운로드” 버튼이 이 번호로 /apicomn/fileDownload를 호출하므로, 자료실에서 해당 첨부가 지워지면 양식 다운로드가 깨짐.

인증·권한 구성

  • 세션 미사용(SessionCreationPolicy.STATELESS). JWT 액세스 토큰은 쿠키 accessToken, 리프레시 토큰은 refreshToken에 저장됨(libs/vue-cookies.js:20-60). 관리자 정보는 쿠키 mngrInf, 현재 메뉴는 currentMenu
  • 토큰 유효기간은 액세스 7일, 리프레시 14일임. 프론트 쿠키 만료도 같음(auth/jwt/jwtDefaultConfig.js:31-38). 주석에는 “30분”이라 적혀 있으나 실제 값은 7일임
  • 인증 없이 허용되는 경로는 /, /login, /api/v1/cert/*, 그리고 POST /api/v1/cert/fileDownload(공지 첨부 다운로드)뿐임. 나머지는 anyRequest().denyAll()임(config/security/SecurityConfig.java:88-95)
  • 권한은 DB에서 읽어 기동 시점에 Security 매처로 등록됨. cmn.tb_rol_mnu_r(역할-메뉴-기능) × cmn.tb_mnu_m(메뉴의 업무ID tsk_id)를 조인해 역할 → /api/v1/{tsk_id}/** + HTTP 메서드 권한을 만듦. 기능코드 = HTTP 메서드 대응임
메뉴기능코드HTTP 메서드API 접두 관례
CPOSTinput...
RGETfind...
UPUTmodify...
DDELETEdelete...

근거 config/security/SecurityConfig.java:112-175, mapper-rdb/apicomn/ApiComn_Mapper.xml:7-32

  • 권한이 URL 접두(/api/v1/{업무ID}/**) + 메서드 단위라 같은 업무ID를 공유하는 API는 권한이 함께 움직임. 예로 /api/v1/apart/**에 공지사항·관리비·임대료·전자투표·DR·거점관리소직원이 모두 들어 있음
  • 역할·메뉴 권한을 DB에서 바꾸면 애플리케이션 재기동이 필요함(기동 시 setMatcherWithAuthorityUserbyRole() 1회 실행)
  • CSRF 비활성, XSS 헤더 활성, CORS는 cors.allowed-origins 값으로 제한됨. 웹 접속 제한 정책은 공개노트/개발/웹 접속 제한
  • 모든 컨트롤러 호출을 AOP(SQIFrameworkAspect)가 감쌈. 요청 URI·파라미터·응답·수행시간을 로그로 남기고 SFWExceptionresCd/resMsg로 변환함. 예외 메시지는 코드&치환값 형식(구분자 &)이고 MessageFormat으로 {1} 자리에 치환값이 들어감(aop/SQIFrameworkAspect.java:55-115, comn/constants/DelimeterConstants.java)
    • 로그 검색 키워드: [SMAH-Framework], Request URI =, SQIFrameworkException :, Excution Time :
  • 트랜잭션은 AOP로 kr.or.lh.smah..*Service.*에 일괄 적용됨(aop/TransationAspect.java:49)

화면(메뉴) 목록

메뉴는 DB에서 옴

좌측 메뉴는 코드에 없음. 로그인 후 GET /api/v1/apicomn/findNavMenuList로그인 사용자의 역할(rolId)에 매핑된 메뉴만 내려받아 트리로 만듦(store/module/cmn/cmnStore.js:213-219). 쿼리는 cmn.tb_rol_mnu_r + cmn.tb_mnu_m을 조인하고 pgm_ds_cd='LHW', us_yn='Y' 조건을 검(ApiComn_Mapper.xml:35-56). 따라서 메뉴 이름·순서·아이콘·계층은 DB 값이고 코드로는 확정 불가임.

  • 화면 제목도 currentMenu.mnuNm(= DB의 mnu_nm)을 그대로 씀. 예로 views/apt-mng/ervt/ErvtAdd.vue:243${currentMenu.mnuNm} 등록
  • 로그인 직후 이동할 화면도 역할별 DB 값 trm.rol_scrn_pth로 정해짐(views/login/components/LoginFrm.vue:146)
  • 라우트 경로가 내 메뉴 목록에 없으면 “접근 권한이 없습니다.” 알림 후 내 첫 메뉴로 돌려보냄(router/index.js:130-137)
  • 화면 안의 등록·수정·삭제 버튼 노출은 그 메뉴의 mnuFncCds(C/R/U/D)로 제어됨(router/index.js:128, views/apt-mng/rsd-nc/useRsdNc.js:19-36)

라우트 전체 (67개)

라우트 정의는 src/router/routes/*.js, 조립은 src/router/index.js:33-48임.

대메뉴는 아파트 관리(/apt-mng) · 단지 관리(/sbd-mng) · 제어 관리(/cont-mng) · 이벤트 관리(/evn-mng) · 헬스케어(/hc) · 대시보드(/dashboard) · 보고서(/rpt) · 시스템 정보(/sys-inf) 8개이고, 각 화면의 한글 기능명은 공개노트/개발/모듈/LMC 기능 상세에 정리돼 있음. 다만 좌측 메뉴와 화면 제목에 실제로 찍히는 글자는 DB cmn.tb_mnu_mmnu_nm이므로 DB에서 이름을 바꾸면 화면 표기도 같이 바뀜.

공통 cmn.js (인증 불필요)

경로이름하는 일
/-/intro/Intro로 리다이렉트
/intro/IntroIntro로그인 전 소개 페이지. 배너, 공지사항, FAQ, 기능소개, 매뉴얼(PDF·영상), 앱 다운로드, 푸터
/intro/PrivacyPolicyPrivacyPolicy개인정보처리방침 (GET /cert/findSvcClu)
/error-404error-404404
*-error-404로 리다이렉트

로그인·가입 login.js (인증 불필요, layout: full)

경로이름하는 일
/login/LoginLoginFrm로그인. 운영에서는 로그인 후 SMS 2차 인증 모달 표시
/login/Register/RegisterFrmRegisterFrm가입유형 선택: “LH 건설임대 관리소 가입하기” / “LH 매입임대 거점관리소 가입하기” / “Q+매니저 가입하기”
/login/Register/RegisterFrm1RegisterFrm1관리사무소 가입(건설임대 단지 관리소 / 매입임대 거점관리소). 화면 제목 “관리사무소가입”
/login/Register/RegisterFrm2RegisterFrm2Q+매니저 가입(rolId MVIN_MNGR). 화면 제목 “Q+매니저가입”
/login/ResetPwd/ResetPwdFrm1ResetPwdFrm1비밀번호 재설정 1단계 — 아이디 확인
/login/ResetPwd/ResetPwdFrm2ResetPwdFrm22단계 — 휴대폰 SMS 본인인증. 1단계 미이행 시 진입 차단
/login/ResetPwd/ResetPwdFrm3ResetPwdFrm33단계 — 새 비밀번호 입력. 2단계 미이행 시 진입 차단

2·3단계 가드는 직전 라우트 이름과 store.state.login.loginStore.mobCtfMngrId를 함께 봄. 어기면 alert('본인 인증 후 비밀번호를 재설정해주세요.') 후 1단계로 되돌림(router/routes/login.js:88-125).

대시보드 dashboard.js

경로이름대상 역할
/dashboard/Dashboard/LhMngrLhMngrLH 본사·지역본부 관리자
/dashboard/Dashboard/FoMngrFoMngr거점관리소 관리자(매입임대)
/dashboard/Dashboard/SbdMngrSbdMngr단지 관리자(건설임대)

LhMngr로 들어와도 검색조건에 grpSn+sbdId가 있으면 SbdMngr로, foMgofId가 있으면 FoMngr로 자동 전환됨(router/index.js:180-190). 대시보드 지표 정의는 공개노트/개발/대시보드 세대수와 단지 현황.

단지 관리 /sbd-mng (16개)

경로이름하는 일
/sbd-mng/arc-inf/ArcInfListArcInfList건축 정보 관리 목록
/sbd-mng/arc-inf/ArcInfDtlArcInfDtl건축 정보 상세
/sbd-mng/arc-inf/ArcInfUpdArcInfUpd건축 정보 수정 (SYS_MNGR에게만 보이는 항목 있음)
/sbd-mng/hnsvr-mng/HnsvrListHnsvrList홈넷 서버 목록 + 연동시험·연동 시작 버튼
/sbd-mng/hnsvr-mng/HnsvrDtlHnsvrDtl홈넷 서버 상세 + 보수내역
/sbd-mng/hnsvr-mng/HnsvrAddHnsvrAdd홈넷 서버 등록 (HNETCO_ISTLR에게만 보이는 항목 있음)
/sbd-mng/hnsvr-mng/HnsvrUpdHnsvrUpd홈넷 서버 수정
/sbd-mng/hsh-inf/HshListHshList세대 정보 목록 / 입주민 승인 대기 조회
/sbd-mng/hsh-inf/HshDtlHshDtl세대 상세 + 앱 사용자 + 전입·전출·반려 처리
/sbd-mng/hsh-inf/HshUpdHshUpd세대 정보 수정
/sbd-mng/fns-dvc-mng/FnsDvcMngListFnsDvcMngList층간소음 기기 목록
/sbd-mng/fns-dvc-mng/FnsDvcMngDtlFnsDvcMngDtl층간소음 기기 상세(펌웨어 전·후 탭)
/sbd-mng/fns-dvc-mng/FnsDvcMngAddFnsDvcMngAdd층간소음 기기 등록
/sbd-mng/fns-dvc-mng/FnsDvcMngUpdFnsDvcMngUpd층간소음 기기 수정
/sbd-mng/wallpad-mng/WallpadListWallpadList세대 단말기(월패드) 목록 + 교체 이력
/sbd-mng/opas-dvc-mng/OpasDvcListOpasDvcList원패스 장치 목록. 컬럼 구성은 공개노트/개발/관리자시스템 원패스 기기 목록

아파트 관리 /apt-mng (19개)

경로이름하는 일
/apt-mng/dr-goo-xpn/DrGooXpnListDrGooXpnListDR(수요반응) 발령 현황 목록
/apt-mng/dr-goo-xpn/DrGooXpnDtlDrGooXpnDtlDR 발령 상세(세대별 발령·참여 탭 2개)
/apt-mng/ervt/ErvtListErvtList전자투표 목록
/apt-mng/ervt/ErvtAddErvtAdd전자투표 등록
/apt-mng/ervt/ErvtUpdErvtUpd전자투표 수정
/apt-mng/ervt/ErvtXpnErvtXpn전자투표 진행 현황(차트)
/apt-mng/ervt/ErvtRslErvtRsl전자투표 결과(차트)
/apt-mng/mngexp/MngexpListMngexpList관리비 등록 현황 목록
/apt-mng/mngexp/MngexpDtlMngexpDtl관리비 세대별 상세
/apt-mng/mngexp/MngexpEdtMngexpEdt관리비 등록(엑셀 업로드)
/apt-mng/rfe/RfeListRfeList임대료 등록 현황 목록
/apt-mng/rfe/RfeDtlRfeDtl임대료 세대별 상세
/apt-mng/rfe/RfeEdtRfeEdt임대료 등록(엑셀 업로드)
/apt-mng/rsd-nc/RsdNcListRsdNcList입주민 공지사항 목록
/apt-mng/rsd-nc/RsdNcDtlRsdNcDtl입주민 공지사항 상세
/apt-mng/rsd-nc/RsdNcAddRsdNcAdd입주민 공지사항 등록
/apt-mng/rsd-nc/RsdNcUpdRsdNcUpd입주민 공지사항 수정
/apt-mng/rsd-nc/RsdNcPreviewRsdNcPreview공지 미리보기(앱 화면처럼 보여주는 전체화면 팝업). requiresAuth 없음, layout: full
/apt-mng/fo-mgof-hw-mng/FoMgofHwMngListFoMgofHwMngList거점관리소 직원 계정 관리(상태변경·삭제)

관리비 엑셀 파싱은 공개노트/개발/관리비 등록 엑셀 파싱, 공지 미리보기 구현은 공개노트/개발/공지사항 미리보기.

제어 관리 /cont-mng (5개)

경로이름하는 일
/cont-mng/dvc-cont-anl/DvcContAnlDvcContAnl기기 제어 분석(홈넷사별 / IoT 기기유형별 차트 2탭)
/cont-mng/dvc-cont-xpn/DvcContXpnListDvcContXpnList기기 제어 현황 목록
/cont-mng/dvc-cont-xpn/DvcContXpnDtlDvcContXpnDtl기기 제어 상세(요청·응답 값)
/cont-mng/dvc-schdl-xpn/DvcSchdlXpnListDvcSchdlXpnList기기 스케줄링 현황
/cont-mng/dvc-schdl-hst/DvcSchdlHstListDvcSchdlHstList기기 스케줄링 이력

이벤트 관리 /evn-mng (3개)

경로이름하는 일
/evn-mng/evn-anl/EvnAnlEvnAnl이벤트 분석(차트)
/evn-mng/evn-hst/EvnHstListEvnHstList이벤트 이력 + 조치내역 등록 모달
/evn-mng/sms-sndg-hst/SmsSndgHstListSmsSndgHstListSMS 발송 내역 + 수신자 모달

헬스케어 /hc (4개)

경로이름하는 일
/hc/cnts-mng/CntsMngListCntsMngList건강생활정보 컨텐츠 목록
/hc/cnts-mng/CntsMngDtlCntsMngDtl컨텐츠 상세
/hc/cnts-mng/CntsMngAddCntsMngAdd컨텐츠 등록
/hc/cnts-mng/CntsMngUpdCntsMngUpd컨텐츠 수정

보고서 /rpt (2개)

경로이름하는 일
/rpt/opr-rpt/OprRpt/LhMngrRptLhMngrRptLH 관리자용 운영보고서(PDF 내보내기)
/rpt/opr-rpt/OprRpt/SbdMngrRptSbdMngrRpt단지 관리자용 운영보고서

시스템 정보 /sys-inf (5개)

경로이름하는 일게시물유형코드
/sys-inf/arcv/ArcvListArcvList자료실 목록ARC
/sys-inf/arcv/ArcvDtlArcvDtl자료실 상세(첨부 다운로드)ARC
/sys-inf/faq/FaqListFaqListFAQ 목록(아코디언, 상세 모달)FAQ
/sys-inf/sys-nc/SysNcListSysNcList시스템 공지사항 목록ANN
/sys-inf/sys-nc/SysNcDtlSysNcDtl시스템 공지사항 상세ANN
  • 자료실·FAQ·시스템공지는 같은 API GET /cert/findSystemList / GET /cert/findSystemDetail 을 쓰고 natTpCd(ARC/FAQ/ANN)로만 구분됨
  • 상세 분류는 natDtlTpCd(코드그룹 NAT_DTL_TP_CD), 검색조건 conditionT(제목 like) / W(작성자 일치)임
  • 근거 api/sys-inf/arcvApi.js:9, faqApi.js:35, sysNcApi.js:61, mapper-rdb/system/System_Mapper.xml:7-36
  • 인트로(비로그인) 화면도 같은 API를 인증 없이 호출함(api/intro/introApi.js:5-35). 2026-07-07 커밋 688f96eb·daa48de8(감사원 보안 취약점 조치)에서 공개용 조회를 별도 서비스(findPublicSystemList / findPublicSystemDetail)로 분리함

LMC 메뉴가 아닌 것

  • 방문주차 / 입출차 / 하자(바로처리) — 인트로 페이지의 앱 기능 소개 배너 문구에만 있음(views/intro/components/TheBanner.vue, FunctionIntro.vue). 관리자 메뉴·API·테이블 없음. 입주민 앱 기능임
  • 푸시 발송 화면 — 없음. 푸시는 세대 전입·전출 승인 시 자동으로 1건 나가는 것뿐임. 수동 푸시 발송 메뉴 없음
  • public/mockup/ — 박람회용 목업 HTML(apart-management.html, device-control.html, energy.html, healthcare.html 등). 커밋 1cf1d8bd에서 삭제 후 0d985994(“목업 웹 - 로그인 한 사람만 접속 가능하게 변경”), 40d4d40b(“박람회용 목업 코드 정리”)로 정리됨. 실제 기능 아님

역할(rolId)과 화면 차이

역할 값은 DB cmn.tb_rol_m에 있음. 코드에서 분기에 쓰이는 값은 아래와 같음.

rolId코드상 의미근거
SYS_MNGR시스템 관리자. 건축정보 수정 화면의 추가 항목이 이 역할에만 보임views/sbd-mng/arc-inf/ArcInfList.vue:68, ArcInfUpd.vue:201,234,259,282,303,326
ARA_HDQ_MNGR지역본부 관리자. 지역본부(grpSn) 고정, 단지는 선택 가능components/cmn/CmnFrmSbdFl.vue:422-426
FO_MNGR거점관리소 관리소장(매입임대). 가입 시 선택views/login/components/RegisterFrm1.vue:312
FO_HW거점관리소 관리소직원(매입임대)RegisterFrm1.vue:313
SBD_MNGR단지 관리자(건설임대 관리사무소)RegisterFrm1.vue:375,388
MVIN_MNGRQ+매니저(입주지원 관리자)RegisterFrm2.vue:288
MVIN_SPPT입주지원팀장. 이벤트 권한 없음router/index.js:151
HNETCO_ISTLR홈넷사 설치자. 이벤트 권한 없음, 홈넷서버 등록 화면에 전용 항목router/index.js:151, views/sbd-mng/hnsvr-mng/HnsvrAdd.vue:144
FNS_ISTLR층간소음 설치자. 이벤트 권한 없음router/index.js:151
HHDR / VSTR세대주 / 게스트. 앱 사용자 역할이고 관리자 역할 아님. 전입 승인 시 HHDR, 전출·반려 시 VSTR로 바뀜HouseholdServiceImpl.java:148-152

역할별 검색 필터 고정 규칙

근거 components/cmn/CmnFrmSbdFl.vue:397-426, views/dashboard/components/cmn/GrpSbdFl.vue:313

역할임대유형지역본부단지거점관리소
SBD_MNGR, MVIN_MNGR고정(내 값)고정내 단지 고정-
FO_MNGR, FO_HW고정(매입임대 02)고정선택 가능내 거점관리소 고정
ARA_HDQ_MNGR선택내 지역본부 고정선택선택
그 외(본사·시스템)전부 선택전부 선택전부 선택전부 선택
  • SBD_MNGR는 대시보드에서 지역본부·단지 선택 필터 자체가 보이지 않음(views/dashboard/Dashboard.vue:3, components/cmn/GrpSbdFl.vue:3)
  • MVIN_SPPT, HNETCO_ISTLR, FNS_ISTLR, MVIN_MNGR는 이벤트 코드 목록을 아예 불러오지 않음(router/index.js:151)
  • 입주민 공지사항 등록 화면은 rolIdFO_로 시작하면(매입임대 거점관리소) 대상 선택 UI가 완전히 달라짐(전체·시군구·건물 범위 선택). 건설임대 단지관리자는 자기 단지 고정임(views/apt-mng/rsd-nc/RsdNcAdd.vue:9-148,317)
  • 헬스케어 컨텐츠 등록·수정은 SBD_MNGR/FO_MNGR/FO_HW일 때 단지·거점관리소 행이 고정 표시됨(views/hc/cnts-mng/CntsMngAdd.vue:58,77,90)

서버 측 범위 강제

세대(입주민) 기능은 클라이언트가 보낸 단지·거점관리소 값을 믿지 않고 SecurityContext의 로그인 ID로 다시 조회해 덮어씀.

  • SBD_MNGR/MVIN_MNGR → 본인 sbdId로 고정
  • FO_MNGR/FO_HW → 본인 foMgofId + 임대유형 02로 고정
  • ARA_HDQ_MNGR → 본인 grpSn으로 고정
  • 이후 countAccessibleHousehold로 담당 범위 내 세대인지 한 번 더 확인하고, 아니면 SQI1001(권한 없음)을 던짐
  • 근거 lhmngr/household/service/HouseholdServiceImpl.java:213-259, 커밋 02cf416f(2026-07-09)

이 범위 검증은 세대(Household) 기능에만 있음. 관리비·임대료·공지사항·단말기 등 다른 기능은 화면에서만 필터를 고정하고 서버에서 재검증하지 않음.

관리자 계정

가입 흐름 (/login/Register/...)

운영팀 안내용 절차는 기본매뉴얼/관리사무소/관리자 계정 신청과 승인은 어떻게 하나요.

  1. 아이디 중복확인POST /api/v1/cert/checkId
    • 아이디 형식 ^[a-zA-Z0-9_.\-]+$. 위반 시 SQI0017
    • 없는 아이디면 그 자리에서 cmn.tb_mngr_m에 임시 행이 생김. rolId='SBD_MNGR', 비밀번호는 아이디를 해시한 값, 상태 PRE(가입 진행 중). 근거 CertService.java:73-84
    • 결과 코드: 사용 가능 SQI1007, 이미 사용 중 SQI1008
  2. 휴대폰 SMS 인증POST /cert/certSmsPOST /cert/checkSms
    • 6자리 숫자를 만들어 SMS_CFM_CHR에 저장하고 내부 API POST http://localhost:8084/api/smh/sms로 발송함. 제목 LH스마트홈OTP인증, 본문 {6자리}를 입력하세요., 이벤트코드 CERT, traceId 접두 LMW:. 근거 CertService.java:400-460
    • 상태가 PRE/JIN이 아닌 계정(= 이미 가입된 계정)은 DB에 저장된 휴대폰번호와 입력값이 같아야 발송됨. 다르면 SQI1014
    • 인증 성공 시 상태 PREJIN(본인인증 완료)
    • 인증번호 만료 시간은 코드에 없음(재설정 흐름의 3분 제한만 있음)
  3. 가입 신청POST /api/v1/cert/join
    • 상태가 JIN일 때만 진행됨. PRESQI1010, USESQI0003, 아이디 확인을 안 했으면 SQI1012
    • 매입임대 거점관리소에 이미 FO_MNGR(관리소장)가 있는데 또 FO_MNGR로 가입 시도하면 SQI0020
    • 저장 시 비밀번호는 SHA-512(아이디를 salt로 사용), 실패횟수 0, 비밀번호 만료일 = 오늘 + 6개월, 휴대폰·전화번호는 ARIA 암호화(하이픈 제거 후), 상태 RDY(승인대기)
    • 화면 문구: “회원가입 신청 하시겠습니까?” → “신청이 완료 되었습니다.”
    • 근거 CertService.java:146-232

암호화 알고리즘 상세는 공개노트/개발/모듈/비밀번호와 전화번호 암호화.

계정 상태 코드 (MNGR_SS_CD, 코드그룹 USR_SS_CD)

로그인 시 동작
PRE아이디만 선점, SMS 인증 전로그인 불가 (SQI9002)
JIN본인인증 완료, 가입 신청 전로그인 불가 (SQI9002)
RDY승인대기SQI1003 “사용자의 계정이 승인대기 상태 입니다. 관리자에게 문의하세요.”
USE사용 중정상
LCK잠김SQI1004 “사용자의 계정이 잠김 상태 입니다. 비밀번호 재 설정후 로그인 하세요.”
STP중지SQI1006 “사용자의 계정이 중지 상태 입니다. 관리자에게 문의하세요.”
(승인거부)코드 SQI1005는 있으나 로그인 분기에는 없음SQI1005 “사용자의 계정이 승인거부 상태 입니다. 관리자에게 문의하세요.”

근거 api/cert/controller/CertController.java:211-227

누가 RDYUSE로 승인하는지

LMC 코드에서 cmn.tb_mngr_mmngr_ss_cd를 바꾸는 경로는 PUT /api/v1/apart/modifyFoMgofHwSs 하나뿐임. 화면은 /apt-mng/fo-mgof-hw-mng/FoMgofHwMngList의 “선택 승인”(USE) / “선택 잠금”(LCK) 버튼임.

  • 목록 쿼리가 rol_id='FO_HW' + 로그인 계정의 fo_mgof_id 로 고정돼 있음(FoMgof_Mapper.xml:27-28) → 매입임대 거점관리소 직원만 LMC에서 승인 가능함
  • FO_MNGR(거점관리소장) · SBD_MNGR(건설임대 단지관리자) · MVIN_MNGR(Q+매니저) · ARA_HDQ_MNGRRDYUSE 승인 화면·API는 LMC 코드에 없음
  • 승인·잠금 처리 시 상태만 바꾸는 게 아니라 비밀번호 실패횟수가 0으로 초기화되고 만료일이 오늘+6개월로 다시 잡힘(FoMgofServiceImpl.java:61-62)
  • 삭제는 cmn.tb_mngr_m 물리 삭제이고 복구 불가
  • 현재 승인·잠금 버튼은 처리가 되면서도 오류 팝업이 뜸(아래 알려진 이슈) → 운영 안내는 기본매뉴얼/관리사무소/관리자 계정 신청과 승인은 어떻게 하나요

로그인

POST /api/v1/cert/login (CertController.java:192-250)

  1. 아이디로 조회함. 없으면 SQI0010
  2. 상태가 USE가 아니면 위 표대로 거절됨
  3. 비밀번호 비교(SHA-512, salt=아이디). 틀리면 실패 횟수 +1SQI1000
  4. 5회째 실패 시 상태가 LCK(잠김)로 바뀜. 코드상으로는 mngrPwdFailCnt == 4인 상태에서 또 틀릴 때 잠김(CertService.java:236-246)
  5. 성공하면 JWT 발급 후 접속 로그(cmn.tb_mngr_lgn_l)에 아이디 / 일시 / 접속 IP / 시스템구분 LMW 기록됨
  6. 비밀번호 만료일(MNGR_PWD_EPI_YMD)이 오늘보다 이전이면 isPwdExp='Y'가 응답에 들어가고 프론트가 “비밀번호 변경 안내” 모달을 띄움
  7. 이미 다른 곳에서 로그인 중(DB에 refresh token 존재)이면 isLogin='Y' → 프론트가 “회원님의 아이디는 이미 로그인 중입니다.” + 버튼 “기존 로그인 해제하기” 확인창을 띄움. 확인하면 기존 토큰을 갱신해 기존 세션이 끊김(views/login/components/LoginFrm.vue:113-125)

SMS 2차 인증은 운영(production) 빌드에서만 동작함. 개발 빌드는 건너뜀(views/login/components/LoginFrm.vue:102-107).

if (process.env.NODE_ENV === 'production') { proxy.$modal.show('login-mob-ctf-modal'); }
else { handleCtfSuccess(); }

비밀번호 규칙

서버 정규식(CertService.java:498-506):

^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}|(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{10,}$

위반 시 SQI0016 “비밀번호 형식 조건은 다음과 같습니다: 8자리 이상 3종류 이상(영문,숫자,특수문자) 또는 10자리 이상 2종류(영문,숫자,특수문자 중 2개).”

  • 실제 정규식은 8자 이상 + 영문·숫자·특수문자(@$!%*#?&) 전부 포함, 또는 10자 이상 + 영문·숫자만 허용함. 안내 문구의 “2종류”는 사실상 영문+숫자 조합만 가리키고 “특수문자+숫자 10자리”는 통과하지 않음. 문의가 오면 이 점을 먼저 확인할 것
  • 허용 특수문자는 @ $ ! % * # ? & 8종뿐임. -, _, ~, ^ 등은 통과하지 않음
  • 프론트 안내는 “비밀번호는 8자 이상 입력해주세요.”로 더 느슨함
  • 만료 주기는 6개월임. 신규가입·비밀번호변경·비밀번호재설정·만료연장 모두 오늘+6개월로 다시 잡힘(DateUtil.getCustomDate("M","6","yyyymmdd"))
  • 만료 시 “6개월 동안 비밀번호를 변경하지 않으셨습니다.” 모달이 뜸. 변경하거나 PUT /cert/unsetPassword연장(만료일만 6개월 미룸) 가능함
  • 비밀번호 변경(PUT /cert/changePassword)은 기존 비밀번호를 검증함. 신규·확인 불일치는 SQI0011
  • 비밀번호 재사용 금지·이력 검사 없음

비밀번호 재설정 (잠금 해제 경로)

PUT /api/v1/cert/resetPassword (CertService.java:250-310)

  1. SMS 인증을 마치고 SMS_CFM_CHRRPW 로 표시돼 있어야 함. 아니면 SQI1010
  2. 인증 후 3분이 지나면 무효임updDttm이 3분보다 오래되면 SQI1010
  3. 신규·확인 불일치 시 SQI0011
  4. 성공 시 비밀번호 갱신, 실패횟수 0 초기화, 상태가 LCK이면 USE로 자동 복구, 만료일 +6개월, SMS_CFM_CHR 비움

계정 잠김 문의는 “비밀번호 재설정”을 안내하면 스스로 풀림. 관리자 개입 불필요함.

세션 만료·IP 제한

  • 세션 없음(JWT). 액세스 토큰 7일, 리프레시 토큰 14일
  • 라우트 이동마다 POST /api/v1/token/checkRefreshToken으로 리프레시 토큰을 검증함. 실패(SQI3000)면 서버 메시지를 띄우고 로그인 화면으로 보냄(router/index.js:98-113)
  • 401 응답이 오면 /cert/refresh로 액세스 토큰을 재발급하고 원 요청을 재시도함. 재발급도 실패하면 상태 초기화 후 로그인 화면으로 보냄(auth/jwt/jwtService.js:82-102)
  • 리프레시 토큰 검증은 2026-07-09 커밋 4bd69924에서 mngrId 존재 여부 → mngrId+토큰 조합 검증으로 강화됨. 이전에는 클라이언트가 보낸 mngrId를 바꿔 다른 관리자로 행세하는 것이 가능했음
  • IP 제한(화이트리스트) 기능 없음. 접속 IP는 기록만 됨. X-Forwarded-ForProxy-Client-IPWL-Proxy-Client-IPHTTP_CLIENT_IPHTTP_X_FORWARDED_FORgetRemoteAddr() 순으로 읽고, 콤마가 있으면 첫 번째 값을 씀(CertController.java:792-810)
  • 개인정보 접근 이력(POST /user/inputUserRecord)과 시스템 접속내역(POST /user/inputSystemRecordcmn.tb_sys_conn_l)도 같은 방식으로 IP를 기록함. 메뉴를 이동할 때마다 접속내역이 남음(store/module/cmn/cmnStore.js:221-230)

알려진 이슈

지금 기능이 안 되는 것 (치명)

근거·재현 조건은 공개노트/개발/모듈/LMC 기능 상세의 “알려진 이슈” 표에 있음.

증상근거
전입 승인 시 “시스템 오류입니다”가 뜰 수 있음전입 승인이 호출하는 SQL 문장(findMoveInApprovalTarget)이 어느 매퍼 XML에도 없음. 관련 테스트도 실패 상태임. 추정: 매퍼 XML이 빠진 미완성 커밋(73876693)
전자투표 삭제 버튼을 누르면 무조건 “시스템 오류입니다”삭제 시 필수값이 elcVtSn 하나뿐인데 서버가 시작일시를 먼저 숫자로 변환함 → NumberFormatException → HTTP 500
전자투표 수정 화면의 저장이 눌리지 않음ErvtUpd.vue:283이 선언되지 않은 변수를 참조해 저장 함수가 즉시 중단됨
거점관리소 직원 계정 승인·잠금 시 SQI0004 팝업이 뜨지만 상태는 실제로 바뀜결과값을 채우는 줄이 주석 처리돼 반환값이 항상 -1 임(FoMgofServiceImpl.java:67)
헬스케어 컨텐츠 수정 저장이 동작하지 않음(건강정보 외 유형)CntsMngUpd.vue:264가 선언되지 않은 변수를 참조함
이벤트 조치내역을 저장해도 이벤트 상태가 바뀌지 않고 조치 팝업이 늘 빈 폼임조회 결과 바인딩과 evnOccSn 전송이 주석 처리됨(EvnActnHstModal.vue:114-120,139-146)
전출·반려 이력의 세대ID가 항상 NULL, 동거인 수가 0세대ID를 NULL로 만든 직후 그 값을 다시 읽어 이력에 넣는 순서 결함(Household_Mapper.xml:207-245)

보안

  • TLS 인증서 재발급 시 CA 개인키 패스프레이즈가 프로세스 커맨드라인 인자로 들어가고 명령 문자열 전체가 log.debug로 남음. DEBUG 레벨 운영 시 로그에 남음(FileService.java:332-419)
  • 홈넷 서버 상세 화면이 홈넷 서버 Secret과 LH 플랫폼 Secret을 평문으로 보여주고 복사 버튼까지 둠(HnsvrDtl.vue:81-103). 화면 캡처·공유 시 주의 → 기본매뉴얼/운영/보안 점검

그 밖

  • JWT 액세스 토큰 유효기간이 7일임. 주석의 “30분”과 실제 값이 다름. 관리자 PC를 공용으로 쓰면 로그아웃 없이 7일간 접근 가능함
  • 역할·메뉴 권한을 DB에서 바꿔도 재기동 전에는 반영되지 않음. 권한 변경 요청을 처리한 뒤 “아직 안 보인다”는 문의가 이어질 수 있음
  • 권한 단위가 업무ID 접두 + HTTP 메서드라 같은 업무ID의 API가 함께 열리고 닫힘. /api/v1/apart/** 하나에 공지사항·관리비·임대료·전자투표·DR·거점관리소직원이 묶여 있음
  • X-Forwarded-For를 그대로 신뢰함. 프록시 앞단에서 이 헤더를 정리하지 않으면 클라이언트가 임의 IP를 써 넣을 수 있음. 접속 로그의 IP가 위조될 여지가 있음
  • 서버 측 범위 재검증이 세대(Household) 기능에만 있음. 관리비·임대료·공지사항·단말기는 화면 필터만 고정됨
  • 비밀번호 안내 문구와 실제 정규식이 불일치함. “10자리 이상 2종류”는 영문+숫자 조합만 통과함
  • SMS 인증번호 만료 시간이 가입 흐름에는 없음. 재설정 흐름의 3분 제한만 존재함
  • 비밀번호 재사용 이력 검사 없음
  • 아이디 중복확인만 해도 cmn.tb_mngr_mPRE 상태 행이 남음. 가입을 끝내지 않은 유령 행이 쌓임
  • MNGEXP_FORM_FILE_SEQ / RFE_FORM_FILE_SEQ가 첨부 일련번호 하드코딩임. 자료실에서 그 첨부를 지우면 양식 다운로드가 깨짐
  • 인트로(비로그인) 화면이 공지·FAQ·자료실 조회 API를 인증 없이 호출함. 2026-07-07 커밋에서 공개용 서비스로 분리했으나 노출 범위 재확인 필요함
  • 입주민 공지 “알림 등록 일시 이후 수정·삭제 차단” 검증이 주석 처리돼 있어 동작하지 않음
  • 전자투표 삭제·수정은 현재 화면에서 아예 되지 않음(위 치명 이슈 2건). 운영 안내는 기본매뉴얼/관리사무소/전자투표 항목이 삭제 수정이 안 돼요
  • frontend-vue3가 저장소에 있으나 템플릿 원본 상태임. 운영본으로 오인 금지

확인 필요

확인 필요

  • 역할별 메뉴 매핑(어느 역할에 어느 메뉴가 붙어 있는지)은 cmn.tb_rol_mnu_r 테이블 조회로만 확정 가능함
  • 원본이 입주민 앱을 “SMC”로 표기했으나 공개노트/개발/시스템 구성에서 SMC는 시스템관리자 웹임. LMC와 DB를 공유하는 대상이 입주민 앱인지 SMC인지 확인 필요
  • frontend-vue3 마이그레이션 계획과 일정
  • JWT 액세스 토큰 7일이 의도인지, 주석대로 30분으로 줄일 계획이 있는지
  • 인트로 비로그인 조회 API의 노출 범위가 2026-07-07 조치로 충분한지

관련

공개노트/개발/모듈/LMC 기능 상세 공개노트/개발/모듈/LMC 결과 코드와 안내 문구 공개노트/개발/시스템 구성 공개노트/개발/배포와 빌드 공개노트/개발/모듈/APW 연계 서버 공개노트/개발/모듈/비밀번호와 전화번호 암호화 공개노트/개발/웹 접속 제한 공개노트/개발/관리비 등록 엑셀 파싱 공개노트/개발/공지사항 미리보기 공개노트/개발/관리자시스템 원패스 기기 목록 공개노트/개발/대시보드 세대수와 단지 현황 공개노트/기능/로그인과 세대 승인 기본매뉴얼/관리사무소/관리자 계정 신청과 승인은 어떻게 하나요 기본매뉴얼/관리사무소/관리자 계정으로 로그인이 안 돼요 기본매뉴얼/관리사무소/입주민 승인 처리는 어떻게 하나요