UI 빌드와 설치

UI를 추가한 뒤에도 빌드 명령은 그대로입니다.

cd logpresso-sample-app
mvn clean package

프론트엔드 빌드는 메이븐 빌드 과정에 포함되어 있으므로 별도로 실행하지 않습니다. Node.js를 미리 설치할 필요도 없습니다.

빌드 과정

빌드는 다음 순서로 진행됩니다.

generate-resources
  1. node v20.18.0, npm 10.9.0 다운로드    → src/main/ui/node/
  2. npm install                            → src/main/ui/node_modules/
  3. npm run build (tsc -b && vite build)   → src/main/resources/WEB-INF/
process-resources
  4. src/main/resources 복사                → target/classes/
compile
  5. 자바 소스 컴파일                       → target/classes/
package
  6. OSGi 번들 생성                         → target/*.jar
  7. iPOJO 바이트코드 변조
  8. .app 파일 복사                         → target/*.app

프론트엔드가 generate-resources 단계에 있는 이유는 순서 때문입니다. Vite가 src/main/resources/WEB-INF에 파일을 쓰고, 그다음에 process-resourcessrc/main/resources를 번들로 복사합니다. 프론트엔드 빌드가 더 늦게 실행되면 방금 만든 파일이 번들에 들어가지 못합니다.

아래는 프론트엔드 빌드 단계의 출력입니다.

[INFO] --- frontend:1.15.0:npm (npm-build) @ logpresso-sample-app ---
[INFO] Running 'npm run build' in D:\github\logpresso-app-examples\logpresso-sample-app\src\main\ui
[INFO] > logpresso-sample-app-ui@1.1.2608.0 build
[INFO] > tsc -b && vite build
[INFO] vite v6.4.3 building for production...
[INFO] transforming...
[INFO] 37 modules transformed.
[INFO] rendering chunks...
[INFO] computing gzip size...
[INFO] ../resources/WEB-INF/index.html                   0.42 kB  gzip:  0.27 kB
[INFO] ../resources/WEB-INF/assets/index-DpGY6jAl.css   10.88 kB  gzip:  3.17 kB
[INFO] ../resources/WEB-INF/assets/index-B5j1U51f.js   208.08 kB  gzip: 66.03 kB
[INFO] built in 1.43s

이어서 리소스가 번들로 복사되고 자바 소스가 컴파일됩니다. 복사되는 리소스 5개는 매니페스트, 로고, 그리고 방금 만들어진 WEB-INF 파일 3개입니다.

[INFO] --- resources:3.5.0:resources (default-resources) @ logpresso-sample-app ---
[INFO] Copying 5 resources from src\main\resources to target\classes
[INFO] --- compiler:3.8.1:compile (default-compile) @ logpresso-sample-app ---
[INFO] Compiling 12 source files to D:\github\logpresso-app-examples\logpresso-sample-app\target\classes

마지막으로 번들이 만들어지고, iPOJO가 바이트코드를 변조한 뒤 앱 파일이 복사됩니다.

[INFO] --- bundle:5.1.4:bundle (default-bundle) @ logpresso-sample-app ---
[INFO] Building bundle: D:\github\...\target\logpresso-sample-app-1.1.2608.0.jar
[INFO] --- ipojo:1.12.1.asm8:ipojo-bundle (default) @ logpresso-sample-app ---
[INFO] Bundle manipulation - SUCCESS
[INFO] --- antrun:3.1.0:run (default) @ logpresso-sample-app ---
[INFO]      [copy] Copying 1 file to D:\github\...\target
[INFO] BUILD SUCCESS

산출물

target 디렉터리에 두 개의 파일이 생깁니다.

logpresso-sample-app-1.1.2608.0.jar     103,289 바이트
logpresso-sample-app-1.1.2608.0.app     103,289 바이트

두 파일의 크기가 같습니다. 앱 파일은 번들 JAR을 이름만 바꾸어 복사한 것이기 때문입니다. 즉 앱 파일은 별도의 포맷이 아니라 확장자만 다른 ZIP 파일입니다.

번들에는 UI 관련 파일이 아래와 같이 포함됩니다.

WEB-INF/index.html
WEB-INF/assets/index-DpGY6jAl.css
WEB-INF/assets/index-B5j1U51f.js
sonar_app.json
sonar_app_logo.png

빌드된 WEB-INF/index.html을 열어 보면 Vite 설정의 기준 경로가 스크립트 주소에 새겨진 것을 확인할 수 있습니다.

<script type="module" crossorigin src="/app/sample/assets/index-B5j1U51f.js"></script>
<link rel="stylesheet" crossorigin href="/app/sample/assets/index-DpGY6jAl.css">

빌드 후 이 파일을 확인하는 습관을 들이는 것이 좋습니다. 경로가 /app/{앱 코드}/로 시작하지 않으면 설치 후 화면이 비어 있게 되며, 원인을 브라우저에서 찾으려면 시간이 걸립니다.

설치

웹 콘솔의 메뉴에서 .app 파일을 업로드하여 설치합니다. 설치가 끝나면 앱 메뉴에 매니페스트에 등록한 항목이 나타납니다.

앱을 설치한 뒤 접속 프로파일을 등록해야 화면이 데이터를 표시할 수 있습니다. 앱 예제의 화면은 샘플 유형의 접속 프로파일에서 엔드포인트와 API 키를 읽습니다. 접속 프로파일 등록 방법은 앱 예제 설치를 참고하시기 바랍니다.

화면을 빠르게 고치는 방법

스타일 한 줄을 고칠 때마다 전체 빌드와 설치를 반복하면 개발 속도가 크게 떨어집니다. UI만 따로 실행하면 파일을 저장하는 즉시 화면이 갱신됩니다.

cd logpresso-sample-app/src/main/ui
npm install
npm run dev

이 방법을 쓰려면 Node.js 20을 직접 설치해야 합니다. 메이븐이 내려받은 src/main/ui/node 디렉터리의 실행 파일을 직접 사용해도 됩니다.

개발 서버는 http://localhost:6100에서 화면을 서빙하고, /api/sonar/sample 요청을 프록시로 전달합니다. 따라서 실제 로그프레소 소나에 접속하여 데이터를 조회할 수 있습니다.

개발 서버에서는 인증을 따로 처리해야 합니다. 앱을 설치해서 실행하면 화면이 웹 콘솔과 같은 주소에서 서빙되므로 로그인 세션이 그대로 적용됩니다. 그런데 개발 서버는 주소가 다르기 때문에 브라우저가 세션 쿠키를 보내지 않습니다. 아무 조치를 하지 않으면 모든 API 호출이 401 Unauthorized로 실패합니다.

로그프레소 소나 REST API는 API 키 인증을 지원하므로, 프록시가 요청에 인증 헤더를 붙이도록 설정합니다. 접속할 로그프레소 주소와 API 키는 .env.local 파일에서 읽습니다.

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '');

  return {
    server: {
      port: 6100,
      proxy: {
        '/api/sonar/sample': {
          target: env.SONAR_URL || 'https://localhost',
          changeOrigin: true,
          secure: false,
          configure: proxy => {
            proxy.on('proxyReq', proxyReq => {
              if (env.SONAR_API_KEY)
                proxyReq.setHeader('Authorization', `Bearer ${env.SONAR_API_KEY}`);
            });
          },
        },
      },
    },
  };
});

loadEnv의 세 번째 인자가 빈 문자열인 것에 이유가 있습니다. Vite는 기본적으로 VITE_로 시작하는 변수만 읽어 클라이언트 코드에 포함시킵니다. 여기서 필요한 값은 개발 서버가 사용하는 설정이며 화면에 포함되어서는 안 되므로, 접두사 제한을 없애고 설정 파일에서 직접 읽습니다.

.env.local 파일을 만들어 값을 지정합니다. 앱 예제에는 .env.example 파일이 있으므로 복사해서 사용합니다.

cmd> cd logpresso-sample-app\src\main\ui
cmd> copy .env.example .env.local
SONAR_URL=https://192.0.2.10
SONAR_API_KEY=발급받은-API-키

API 키를 vite.config.ts에 직접 적지 않습니다. .env.local 파일은 버전 관리에서 제외되어 있으므로 키가 저장소에 올라가지 않습니다. API 키는 비밀번호와 같으므로 설정을 공유하거나 화면을 캡처할 때 포함되지 않도록 주의해야 합니다.

secure: false는 개발 환경에서 사설 인증서로 HTTPS를 사용하는 경우를 위한 설정입니다. 인증서 검증을 생략하므로 개발 서버에만 사용합니다.

개발 서버에서 확인할 수 없는 것이 두 가지 있습니다. 웹 콘솔이 없으므로 화면 이동 메시지를 받지 못하고, 테마는 웹 콘솔 대신 운영체제 설정을 따릅니다. 화면 이동과 테마 연동은 설치한 뒤에 확인해야 합니다.

문제 해결

증상원인해결
화면이 비어 있고 스크립트 요청이 404Vite 기준 경로가 앱 코드와 다름vite.config.tsbase/app/{앱 코드}/로 수정
화면 대신 오류 페이지번들에 WEB-INF가 없음프론트엔드 플러그인이 generate-resources 단계에 있는지 확인
메뉴가 나타나지 않음매니페스트 검증 실패, 앱 코드 형식 오류, 로고 누락sonar_app.json의 필수 항목과 sonar_app_logo.png 존재 확인
앱 시작 직후 NoSuchMethodErrorsonar-app-api 버전이 설치된 플랫폼과 맞지 않음플랫폼 버전에 맞는 sonar-app-api 버전으로 변경
번들 생성 단계에서 빌드 실패maven-bundle-plugin 5.1.5 이상 사용5.1.4로 고정
타입 오류로 빌드 실패tsc -b가 타입 검사에서 중단타입 오류를 수정, 검사를 건너뛰지 않음
번들 크기가 빌드마다 계속 증가이전 빌드의 해시 파일이 남음build.emptyOutDirtrue로 설정
API 호출이 401접속 프로파일의 API 키가 유효하지 않음API 키 재발급 후 접속 프로파일 수정

에이전트 프롬프트

빌드나 설치 문제를 에이전트에게 맡길 때 사용합니다.

로그프레소 앱의 XDR UI 빌드 문제를 진단한다.

증상: (관찰한 내용을 구체적으로 기술)

확인 순서:
1. mvn clean package 출력에서 실패한 단계
2. 산출된 src/main/resources/WEB-INF/index.html의 스크립트 경로가
   /app/{앱 코드}/assets/ 로 시작하는지
3. 번들에 WEB-INF/, sonar_app.json, sonar_app_logo.png 가 포함되었는지
4. vite.config.ts의 base와 outDir
5. pom.xml에서 frontend-maven-plugin이 generate-resources 단계인지,
   maven-bundle-plugin이 5.1.4인지

참조: https://docs.logpresso.com/ko/app-sdk/build-ui-app

원인을 찾으면 수정하고 빌드를 다시 실행하여 확인하라. 추측으로 여러 곳을
동시에 바꾸지 말고 한 번에 하나씩 확인하라.

다음 절에서는 앱 화면을 웹 콘솔 메뉴에 등록하는 방법을 설명합니다.