Skip to content

기술 스택

iFlex 는 NeoSQL 로 생성되는 신규 프로젝트의 템플릿입니다. 템플릿이지만 그대로 실행되도록 구성되어 있어, 생성 직후부터 일반 프로젝트와 동일하게 개발·테스트할 수 있습니다.

처음 오셨다면

버전 표만 훑고 개발 환경 설정 으로 넘어가세요. 아키텍처는 실제로 코드를 작성할 때 Unvus Core 문서에서 자세히 다룹니다.

개발 환경

도구버전비고
JDK21Gradle toolchain 으로 고정 (JavaLanguageVersion.of(21))
Gradle8.14wrapper 포함 — 별도 설치 불필요 (./gradlew)
Node.js22.22+ 또는 24.12+두 프론트엔드 공통 (^22.22.0 || >=24.12.0)

Node 22.22 미만은 동작하지 않습니다

@quasar/app-vite 3 이 ^22.22.0 이상을 요구합니다. Node 20 은 지원 대상에서 빠졌습니다.

bash
nvm install lts/jod   # Node 22.x
node -v               # v22.22.0 이상인지 확인

JDK 는 21 이어야 합니다

Spring Boot 4.x 는 JDK 17 을 지원하지 않습니다. sdkman 으로 멀티 JDK 환경을 구성해 프로젝트별로 전환하는 것을 권장합니다.

bash
sdk install java 21.0.5-tem
sdk use java 21.0.5-tem

실행 시 JVM 옵션이 필요합니다

Spring Boot 실행에는 --add-opens-Duser.timezone=UTC필수입니다. UTC 를 지정하지 않으면 기동이 실패합니다 — 프로젝트는 날짜/시간을 UTC 로 고정해 다루기 때문입니다. 자세한 내용은 개발 환경 설정 을 보세요.

서버 사이드

라이브러리버전
Spring Boot4.0.3
Spring Cloud2024.0.0
Spring SecuritySpring Boot BOM 관리 (JWT / OAuth2 Resource Server)
MyBatis Dynamic SQL2.0.0
MyBatis Spring Boot Starter4.0.1
springdoc-openapi3.0.3
Apache POI5.5.1 (엑셀)
Apache Tika3.3.1 (파일 타입 판별)
P6Spy2.0.0 (SQL 로깅)
Lombok1.18.38

데이터 접근은 MyBatis 로 합니다. ORM 은 쓰지 않습니다. SQL 은 Dynamic SQL 로 조립하고, 결과 매핑(resultMap)은 mapper XML 에 둡니다 → MyBatis Dynamic SQL

트랜잭션은 DataSourceTransactionManager 가 커넥션 단위로 관리하며, MyBatis 매퍼 호출이 @Transactional 경계에 참여합니다. 세션은 Redis, 캐시는 EhCache 를 사용합니다.

지원 DBMS

MariaDB / MySQL / Oracle / PostgreSQL / SQL Server. DDL 은 neo-sql/{dbms}/ 에 DBMS 별로 준비되어 있고, 키 생성 전략은 런타임에 자동 분기합니다 (→ MyBatis Dynamic SQL).

프론트엔드

두 프론트엔드는 공통 의존성 버전을 완전히 일치시켜 운영합니다. 한쪽만 올라가서 생기는 동작 차이를 없애기 위해서입니다.

공통 (버전 동일)

라이브러리버전
Vue3.5
Quasar2.23
@quasar/app-vite3.2 (내부 Vite 8)
Pinia4.0
Vue Router5.2 (hash 모드)
Element Plus2.14
TypeScript5.9
ESLint10
axios / dayjs / vue-final-modal1.18 / 1.11 / 4.5

프로젝트별 차이

cms/frontend (관리자)channel-vue/frontend (사용자)
렌더링CSRSSR (Express)
전용 라이브러리AG Grid, CodeMirror, SunEditor, CKEditor, ApexCharts, Highchartsvue-i18n

렌더링 방식과 화면 성격에 따른 전용 라이브러리만 다르고, 공통 스택은 동일합니다.

  • 컴포넌트는 Composition API + <script setup> 로 작성합니다.
  • 커스텀 컴포넌트는 nv- 접두사를 씁니다 (nv-button, nv-input …).
  • 자세한 규칙은 Vue 3 + TypeScript 참고.

모듈 구조

이건 iFlex 저장소의 구조입니다

iFlex 는 템플릿입니다. NeoSQL 로 실제 고객사 프로젝트를 생성하면 모듈 이름이 바뀌고, 필요 없는 모듈은 아예 빠집니다. 아래 이름을 고정된 것으로 여기지 마세요.

iflex/
├── core/                    공유 라이브러리 (bootJar 비활성)
├── channel/                 PC 웹사이트 (Thymeleaf, WAR)
├── channel-vue/
│   ├── backend/             모바일·최신 웹 REST API
│   └── frontend/            Quasar SSR
├── cms/
│   ├── backend/             관리자 REST API
│   └── frontend/            관리자 UI (CSR)
├── batch/                   배치·스케줄러
├── neo-config/              NeoSQL 프로젝트 생성 설정
├── neo-sql/                 DBMS 별 SQL 스크립트
└── docker/                  환경별 docker-compose

모든 백엔드 모듈은 core 를 의존합니다. 업무 로직·엔터티·플랫폼 코드는 전부 core 에 있고, 각 모듈은 REST 표면과 설정만 갖습니다.

프로젝트 생성 후에는 어떻게 되나

neo-config/neo.conf.js 가 모듈 구성을 정의합니다. 생성 시 선택한 것만, 새 이름으로 만들어집니다.

저장소생성 후 기본 이름개수패키지
corecore필수 1개{base}.core
cmscms0~N개{base}.cms
channelpc-web0~N개{base}.web.pc
channel-vuemobile-web0~N개{base}.web.mobile
batchbatch0~1개{base}.batch

읽는 방법이 셋 있습니다.

  • 이름이 바뀝니다channelpc-web, channel-vuemobile-web 이 기본이고, 생성 시 더 바꿀 수 있습니다
  • 빠질 수 있습니다core 를 뺀 전부가 최소 0개입니다. 배치가 필요 없으면 batch 모듈뿐 아니라 modules/batch·platform/batch 소스와 관련 SQL 까지 함께 제외됩니다
  • 여러 개일 수 있습니다cms·pc-web·mobile-web 은 상한이 없습니다. 운영자용과 협력사용 CMS 를 따로 두는 식으로 같은 종류를 여러 벌 만들 수 있습니다

패키지도 com.unvus.iflex.* 에서 고객사 basePackage 로, 테이블 접두어도 nv_ 에서 지정한 값으로 치환됩니다.

생성 전용 디렉토리는 어떻게 되나

디렉토리생성된 프로젝트에
neo-config/없음 — 템플릿을 만들기 위한 설정이라 결과물에는 넘어가지 않습니다
docker/없음
neo-sql/있음 — 단, 선택한 DBMS 하나만 남습니다 (neo-sql/mysql/neo-sql/)
.neosql/있음 — 이후 엔터티 생성에 계속 쓰는 메타데이터입니다

neo-sql 은 4개 DBMS 스크립트를 다 들고 있다가, 생성 시 고른 하나만 남기고 테이블 접두어까지 치환해 넘어갑니다.

이 문서의 나머지 설명은 저장소 기준 이름(cms, channel-vue …)을 씁니다. 실제 프로젝트에서는 해당 위치의 모듈로 바꿔 읽으세요.

core 의 구조

core 는 두 층으로 나뉩니다. 이 구분이 코드를 읽는 가장 중요한 단서입니다.

core/src/main/java/com/unvus/iflex/core/
├── platform/     프레임워크 — 업무와 무관한 재사용 기반
└── modules/      업무 — 게시판, 회원, 배너, 메시지 …

platform 패키지

업무 코드가 기대는 기반입니다. 신규 기능을 만들 때 여기 있는 것을 먼저 찾아 쓰세요.

패키지역할관련 문서
query검색 조건 트리(NvQuery), 스키마 선언, SQL 조건 조립검색 조건
pagination페이징·정렬 실행(InPagination), 반복 조회(DbIterator)목록과 페이징
support제네릭 리포지토리·서비스(NvRepositoryDynamic), 키 전략MyBatis Dynamic SQL
domain감사 인터페이스(AbstractAuditingEntity), EnumCode, 보안 주체
excel엑셀 생성·읽기(ExcelBuilder)
firo파일 업로드 플랫폼Firo
messaging메일·푸시·SMS 발송
audit감사 로그·액션 로그
web웹 유틸, 인터셉터
util날짜·타임존·컬렉션 등 공용 유틸유틸리티

platformmodules 를 나누나

platform모든 고객사 프로젝트가 공유하는 코드입니다. 여기를 고치면 전 프로젝트에 영향이 갑니다. 반대로 modules 는 프로젝트마다 달라지는 업무 코드입니다. 업무 요구사항을 구현할 때 platform 을 고치고 있다면, 대개는 잘못된 위치에 코드를 쓰고 있는 것입니다.

modules 패키지

업무 도메인 하나가 한 패키지입니다. 내부 구조는 모든 모듈이 동일합니다.

modules/board/
├── entity/          Board.java + support/BoardDsl.java
├── dto/             BoardDto.java, BoardSearchSchema.java
├── repository/      BoardRepository.java
└── service/         BoardService.java
파일역할
{X}.java엔터티 (테이블 1:1)
{X}Dsl.java컬럼 참조용 DSL — 컬럼명을 문자열로 쓰지 않게 해주는 타입 안전 장치
{X}Dto.javaAPI 응답 DTO (엔터티를 직접 노출하지 않습니다)
{X}SearchSchema.java검색 가능 필드 화이트리스트
{X}Repository.java데이터 접근
{X}Service.java업무 로직

날짜/시간 규약

프로젝트 전체가 하나의 규칙을 따릅니다. 처음 합류하면 가장 먼저 알아야 할 규약입니다.

상황사용 타입
시각 (특정 순간)Instant — 항상 UTC
날짜 (달력상 하루)LocalDate
금지LocalDateTime — 타임존이 없어 의미가 모호합니다
  • 저장·전송·서버 내부 연산은 전부 UTC 입니다. 와이어 포맷은 ISO-8601 (2026-07-25T04:12:33Z).
  • 사용자 타임존은 화면에 보여줄 때만 적용합니다.
  • 인자 없는 now() 는 금지입니다 (Instant.now() 는 UTC 라 안전, LocalDate.now() 는 서버 로케일에 의존). 업무상 "오늘" 은 TimeZones.siteDefault() 를 기준으로 구합니다.
java
Instant  createdDt = Instant.now();                    // ✅ 시각
LocalDate today    = LocalDate.now(TimeZones.siteDefault());  // ✅ 업무상 오늘
LocalDateTime bad  = LocalDateTime.now();              // ❌ 금지 (테스트가 잡습니다)

프론트엔드는 useFormat / formatDt 로 표시 시점에 변환합니다.

다음 단계