역할
LMC(LH Manager Web Client)는 LH 본사·지역본부·거점관리소·단지 관리사무소 직원이 쓰는 스마트홈 관리자 웹임. 전체 그림은 공개노트/개발/시스템 구성.
- 관리자가 브라우저로 접속해 단지·세대·단말기·관리비·공지사항·전자투표를 관리함
- 백엔드(
lhmngr.war, Spring Boot)는 PostgreSQLsmahdb를 직접 읽고 씀 - 홈넷 연동·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 호출 대상 |
|---|---|---|---|
| local | src/main/resources-local/ | localhost:3000 | - |
| dev | src/main/resources-dev/ | https://homezdev.lh.or.kr | http://[IP]:8094 |
| prod | src/main/resources-prod/ | https://homez.lh.or.kr | https://homez-api.lh.or.kr:8094 |
- 메뉴 조회용 프로그램 구분 코드
server.config.pgmDsCd=LHW - 근거
LMC/backend/src/main/resources-prod/application.properties:1-80
빌드·배포
빌드 절차는 공개노트/개발/배포와 빌드에도 정리해 둠.
| 명령 | 하는 일 |
|---|---|
./gradlew clean build | 전체 빌드 |
./gradlew bootWar | lhmngr.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 t | ESLint / Jest |
- 프론트는 백엔드와 별도 빌드임
- 백엔드
WebConfig가classpath:/static/에서 파일을 못 찾으면/static/index.html로 포워딩해 SPA 라우팅을 살림(config/web/WebConfig.java:30-44). 즉 프론트 산출물을 백엔드 static에 넣어 war 하나로 배포하는 구성도 가능함. 현재vue.config.js의outputDir는 주석 처리돼 있음 - 운영 빌드는 파일명에 contenthash를 붙임(
vue.config.js:31-33). 배포 직후 기존 탭에서 ChunkLoadError가 나면 라우터가 한 번만 자동 새로고침함(10초 내 반복은 중단,router/index.js:202-228).public/force-reload.js도 있음
프론트 환경 파일
| 파일 | VUE_APP_API_URL | MNGEXP_FORM_FILE_SEQ | RFE_FORM_FILE_SEQ |
|---|---|---|---|
.env.production | https://homez.lh.or.kr/api/v1 | 55 | 56 |
.env.development | https://homezdev.lh.or.kr/api/v1 | 160 | 161 |
*_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(메뉴의 업무IDtsk_id)를 조인해역할 → /api/v1/{tsk_id}/**+ HTTP 메서드 권한을 만듦. 기능코드 = HTTP 메서드 대응임
| 메뉴기능코드 | HTTP 메서드 | API 접두 관례 |
|---|---|---|
| C | POST | input... |
| R | GET | find... |
| U | PUT | modify... |
| D | DELETE | delete... |
근거 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·파라미터·응답·수행시간을 로그로 남기고SFWException을resCd/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_m의 mnu_nm 값이므로 DB에서 이름을 바꾸면 화면 표기도 같이 바뀜.
공통 cmn.js (인증 불필요)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/ | - | /intro/Intro로 리다이렉트 |
/intro/Intro | Intro | 로그인 전 소개 페이지. 배너, 공지사항, FAQ, 기능소개, 매뉴얼(PDF·영상), 앱 다운로드, 푸터 |
/intro/PrivacyPolicy | PrivacyPolicy | 개인정보처리방침 (GET /cert/findSvcClu) |
/error-404 | error-404 | 404 |
* | - | error-404로 리다이렉트 |
로그인·가입 login.js (인증 불필요, layout: full)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/login/Login | LoginFrm | 로그인. 운영에서는 로그인 후 SMS 2차 인증 모달 표시 |
/login/Register/RegisterFrm | RegisterFrm | 가입유형 선택: “LH 건설임대 관리소 가입하기” / “LH 매입임대 거점관리소 가입하기” / “Q+매니저 가입하기” |
/login/Register/RegisterFrm1 | RegisterFrm1 | 관리사무소 가입(건설임대 단지 관리소 / 매입임대 거점관리소). 화면 제목 “관리사무소가입” |
/login/Register/RegisterFrm2 | RegisterFrm2 | Q+매니저 가입(rolId MVIN_MNGR). 화면 제목 “Q+매니저가입” |
/login/ResetPwd/ResetPwdFrm1 | ResetPwdFrm1 | 비밀번호 재설정 1단계 — 아이디 확인 |
/login/ResetPwd/ResetPwdFrm2 | ResetPwdFrm2 | 2단계 — 휴대폰 SMS 본인인증. 1단계 미이행 시 진입 차단 |
/login/ResetPwd/ResetPwdFrm3 | ResetPwdFrm3 | 3단계 — 새 비밀번호 입력. 2단계 미이행 시 진입 차단 |
2·3단계 가드는 직전 라우트 이름과 store.state.login.loginStore.mobCtfMngrId를 함께 봄. 어기면 alert('본인 인증 후 비밀번호를 재설정해주세요.') 후 1단계로 되돌림(router/routes/login.js:88-125).
대시보드 dashboard.js
| 경로 | 이름 | 대상 역할 |
|---|---|---|
/dashboard/Dashboard/LhMngr | LhMngr | LH 본사·지역본부 관리자 |
/dashboard/Dashboard/FoMngr | FoMngr | 거점관리소 관리자(매입임대) |
/dashboard/Dashboard/SbdMngr | SbdMngr | 단지 관리자(건설임대) |
LhMngr로 들어와도 검색조건에 grpSn+sbdId가 있으면 SbdMngr로, foMgofId가 있으면 FoMngr로 자동 전환됨(router/index.js:180-190). 대시보드 지표 정의는 공개노트/개발/대시보드 세대수와 단지 현황.
단지 관리 /sbd-mng (16개)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/sbd-mng/arc-inf/ArcInfList | ArcInfList | 건축 정보 관리 목록 |
/sbd-mng/arc-inf/ArcInfDtl | ArcInfDtl | 건축 정보 상세 |
/sbd-mng/arc-inf/ArcInfUpd | ArcInfUpd | 건축 정보 수정 (SYS_MNGR에게만 보이는 항목 있음) |
/sbd-mng/hnsvr-mng/HnsvrList | HnsvrList | 홈넷 서버 목록 + 연동시험·연동 시작 버튼 |
/sbd-mng/hnsvr-mng/HnsvrDtl | HnsvrDtl | 홈넷 서버 상세 + 보수내역 |
/sbd-mng/hnsvr-mng/HnsvrAdd | HnsvrAdd | 홈넷 서버 등록 (HNETCO_ISTLR에게만 보이는 항목 있음) |
/sbd-mng/hnsvr-mng/HnsvrUpd | HnsvrUpd | 홈넷 서버 수정 |
/sbd-mng/hsh-inf/HshList | HshList | 세대 정보 목록 / 입주민 승인 대기 조회 |
/sbd-mng/hsh-inf/HshDtl | HshDtl | 세대 상세 + 앱 사용자 + 전입·전출·반려 처리 |
/sbd-mng/hsh-inf/HshUpd | HshUpd | 세대 정보 수정 |
/sbd-mng/fns-dvc-mng/FnsDvcMngList | FnsDvcMngList | 층간소음 기기 목록 |
/sbd-mng/fns-dvc-mng/FnsDvcMngDtl | FnsDvcMngDtl | 층간소음 기기 상세(펌웨어 전·후 탭) |
/sbd-mng/fns-dvc-mng/FnsDvcMngAdd | FnsDvcMngAdd | 층간소음 기기 등록 |
/sbd-mng/fns-dvc-mng/FnsDvcMngUpd | FnsDvcMngUpd | 층간소음 기기 수정 |
/sbd-mng/wallpad-mng/WallpadList | WallpadList | 세대 단말기(월패드) 목록 + 교체 이력 |
/sbd-mng/opas-dvc-mng/OpasDvcList | OpasDvcList | 원패스 장치 목록. 컬럼 구성은 공개노트/개발/관리자시스템 원패스 기기 목록 |
아파트 관리 /apt-mng (19개)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/apt-mng/dr-goo-xpn/DrGooXpnList | DrGooXpnList | DR(수요반응) 발령 현황 목록 |
/apt-mng/dr-goo-xpn/DrGooXpnDtl | DrGooXpnDtl | DR 발령 상세(세대별 발령·참여 탭 2개) |
/apt-mng/ervt/ErvtList | ErvtList | 전자투표 목록 |
/apt-mng/ervt/ErvtAdd | ErvtAdd | 전자투표 등록 |
/apt-mng/ervt/ErvtUpd | ErvtUpd | 전자투표 수정 |
/apt-mng/ervt/ErvtXpn | ErvtXpn | 전자투표 진행 현황(차트) |
/apt-mng/ervt/ErvtRsl | ErvtRsl | 전자투표 결과(차트) |
/apt-mng/mngexp/MngexpList | MngexpList | 관리비 등록 현황 목록 |
/apt-mng/mngexp/MngexpDtl | MngexpDtl | 관리비 세대별 상세 |
/apt-mng/mngexp/MngexpEdt | MngexpEdt | 관리비 등록(엑셀 업로드) |
/apt-mng/rfe/RfeList | RfeList | 임대료 등록 현황 목록 |
/apt-mng/rfe/RfeDtl | RfeDtl | 임대료 세대별 상세 |
/apt-mng/rfe/RfeEdt | RfeEdt | 임대료 등록(엑셀 업로드) |
/apt-mng/rsd-nc/RsdNcList | RsdNcList | 입주민 공지사항 목록 |
/apt-mng/rsd-nc/RsdNcDtl | RsdNcDtl | 입주민 공지사항 상세 |
/apt-mng/rsd-nc/RsdNcAdd | RsdNcAdd | 입주민 공지사항 등록 |
/apt-mng/rsd-nc/RsdNcUpd | RsdNcUpd | 입주민 공지사항 수정 |
/apt-mng/rsd-nc/RsdNcPreview | RsdNcPreview | 공지 미리보기(앱 화면처럼 보여주는 전체화면 팝업). requiresAuth 없음, layout: full |
/apt-mng/fo-mgof-hw-mng/FoMgofHwMngList | FoMgofHwMngList | 거점관리소 직원 계정 관리(상태변경·삭제) |
관리비 엑셀 파싱은 공개노트/개발/관리비 등록 엑셀 파싱, 공지 미리보기 구현은 공개노트/개발/공지사항 미리보기.
제어 관리 /cont-mng (5개)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/cont-mng/dvc-cont-anl/DvcContAnl | DvcContAnl | 기기 제어 분석(홈넷사별 / IoT 기기유형별 차트 2탭) |
/cont-mng/dvc-cont-xpn/DvcContXpnList | DvcContXpnList | 기기 제어 현황 목록 |
/cont-mng/dvc-cont-xpn/DvcContXpnDtl | DvcContXpnDtl | 기기 제어 상세(요청·응답 값) |
/cont-mng/dvc-schdl-xpn/DvcSchdlXpnList | DvcSchdlXpnList | 기기 스케줄링 현황 |
/cont-mng/dvc-schdl-hst/DvcSchdlHstList | DvcSchdlHstList | 기기 스케줄링 이력 |
이벤트 관리 /evn-mng (3개)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/evn-mng/evn-anl/EvnAnl | EvnAnl | 이벤트 분석(차트) |
/evn-mng/evn-hst/EvnHstList | EvnHstList | 이벤트 이력 + 조치내역 등록 모달 |
/evn-mng/sms-sndg-hst/SmsSndgHstList | SmsSndgHstList | SMS 발송 내역 + 수신자 모달 |
헬스케어 /hc (4개)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/hc/cnts-mng/CntsMngList | CntsMngList | 건강생활정보 컨텐츠 목록 |
/hc/cnts-mng/CntsMngDtl | CntsMngDtl | 컨텐츠 상세 |
/hc/cnts-mng/CntsMngAdd | CntsMngAdd | 컨텐츠 등록 |
/hc/cnts-mng/CntsMngUpd | CntsMngUpd | 컨텐츠 수정 |
보고서 /rpt (2개)
| 경로 | 이름 | 하는 일 |
|---|---|---|
/rpt/opr-rpt/OprRpt/LhMngrRpt | LhMngrRpt | LH 관리자용 운영보고서(PDF 내보내기) |
/rpt/opr-rpt/OprRpt/SbdMngrRpt | SbdMngrRpt | 단지 관리자용 운영보고서 |
시스템 정보 /sys-inf (5개)
| 경로 | 이름 | 하는 일 | 게시물유형코드 |
|---|---|---|---|
/sys-inf/arcv/ArcvList | ArcvList | 자료실 목록 | ARC |
/sys-inf/arcv/ArcvDtl | ArcvDtl | 자료실 상세(첨부 다운로드) | ARC |
/sys-inf/faq/FaqList | FaqList | FAQ 목록(아코디언, 상세 모달) | FAQ |
/sys-inf/sys-nc/SysNcList | SysNcList | 시스템 공지사항 목록 | ANN |
/sys-inf/sys-nc/SysNcDtl | SysNcDtl | 시스템 공지사항 상세 | ANN |
- 자료실·FAQ·시스템공지는 같은 API
GET /cert/findSystemList/GET /cert/findSystemDetail을 쓰고natTpCd(ARC/FAQ/ANN)로만 구분됨 - 상세 분류는
natDtlTpCd(코드그룹NAT_DTL_TP_CD), 검색조건condition은T(제목 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_MNGR | Q+매니저(입주지원 관리자) | 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)- 입주민 공지사항 등록 화면은
rolId가FO_로 시작하면(매입임대 거점관리소) 대상 선택 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/...)
운영팀 안내용 절차는 기본매뉴얼/관리사무소/관리자 계정 신청과 승인은 어떻게 하나요.
- 아이디 중복확인 —
POST /api/v1/cert/checkId- 아이디 형식
^[a-zA-Z0-9_.\-]+$. 위반 시 SQI0017 - 없는 아이디면 그 자리에서
cmn.tb_mngr_m에 임시 행이 생김.rolId='SBD_MNGR', 비밀번호는 아이디를 해시한 값, 상태PRE(가입 진행 중). 근거CertService.java:73-84 - 결과 코드: 사용 가능 SQI1007, 이미 사용 중 SQI1008
- 아이디 형식
- 휴대폰 SMS 인증 —
POST /cert/certSms→POST /cert/checkSms- 6자리 숫자를 만들어
SMS_CFM_CHR에 저장하고 내부 APIPOST http://localhost:8084/api/smh/sms로 발송함. 제목LH스마트홈OTP인증, 본문{6자리}를 입력하세요., 이벤트코드CERT, traceId 접두LMW:. 근거CertService.java:400-460 - 상태가
PRE/JIN이 아닌 계정(= 이미 가입된 계정)은 DB에 저장된 휴대폰번호와 입력값이 같아야 발송됨. 다르면 SQI1014 - 인증 성공 시 상태
PRE→JIN(본인인증 완료) - 인증번호 만료 시간은 코드에 없음(재설정 흐름의 3분 제한만 있음)
- 6자리 숫자를 만들어
- 가입 신청 —
POST /api/v1/cert/join- 상태가
JIN일 때만 진행됨.PRE면 SQI1010,USE면 SQI0003, 아이디 확인을 안 했으면 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
누가 RDY → USE로 승인하는지
LMC 코드에서 cmn.tb_mngr_m의 mngr_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_MNGR의RDY→USE승인 화면·API는 LMC 코드에 없음- 승인·잠금 처리 시 상태만 바꾸는 게 아니라 비밀번호 실패횟수가 0으로 초기화되고 만료일이 오늘+6개월로 다시 잡힘(
FoMgofServiceImpl.java:61-62) - 삭제는
cmn.tb_mngr_m물리 삭제이고 복구 불가임 - 현재 승인·잠금 버튼은 처리가 되면서도 오류 팝업이 뜸(아래 알려진 이슈) → 운영 안내는 기본매뉴얼/관리사무소/관리자 계정 신청과 승인은 어떻게 하나요
로그인
POST /api/v1/cert/login (CertController.java:192-250)
- 아이디로 조회함. 없으면 SQI0010
- 상태가
USE가 아니면 위 표대로 거절됨 - 비밀번호 비교(SHA-512, salt=아이디). 틀리면 실패 횟수 +1 후 SQI1000
- 5회째 실패 시 상태가
LCK(잠김)로 바뀜. 코드상으로는mngrPwdFailCnt == 4인 상태에서 또 틀릴 때 잠김(CertService.java:236-246) - 성공하면 JWT 발급 후 접속 로그(
cmn.tb_mngr_lgn_l)에 아이디 / 일시 / 접속 IP / 시스템구분LMW기록됨 - 비밀번호 만료일(
MNGR_PWD_EPI_YMD)이 오늘보다 이전이면isPwdExp='Y'가 응답에 들어가고 프론트가 “비밀번호 변경 안내” 모달을 띄움 - 이미 다른 곳에서 로그인 중(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)
- SMS 인증을 마치고
SMS_CFM_CHR가RPW로 표시돼 있어야 함. 아니면 SQI1010 - 인증 후 3분이 지나면 무효임 —
updDttm이 3분보다 오래되면 SQI1010 - 신규·확인 불일치 시 SQI0011
- 성공 시 비밀번호 갱신, 실패횟수 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-For→Proxy-Client-IP→WL-Proxy-Client-IP→HTTP_CLIENT_IP→HTTP_X_FORWARDED_FOR→getRemoteAddr()순으로 읽고, 콤마가 있으면 첫 번째 값을 씀(CertController.java:792-810) - 개인정보 접근 이력(
POST /user/inputUserRecord)과 시스템 접속내역(POST /user/inputSystemRecord→cmn.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_m에PRE상태 행이 남음. 가입을 끝내지 않은 유령 행이 쌓임 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 연계 서버 공개노트/개발/모듈/비밀번호와 전화번호 암호화 공개노트/개발/웹 접속 제한 공개노트/개발/관리비 등록 엑셀 파싱 공개노트/개발/공지사항 미리보기 공개노트/개발/관리자시스템 원패스 기기 목록 공개노트/개발/대시보드 세대수와 단지 현황 공개노트/기능/로그인과 세대 승인 기본매뉴얼/관리사무소/관리자 계정 신청과 승인은 어떻게 하나요 기본매뉴얼/관리사무소/관리자 계정으로 로그인이 안 돼요 기본매뉴얼/관리사무소/입주민 승인 처리는 어떻게 하나요