๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๊ฐœ๋ฐœ ์ผ์ง€ ๐Ÿ‘ฉ‍๐Ÿ’ป

๋Ÿฐํƒ€์ž„ ๋””๋ ‰ํ„ฐ๋ฆฌ ์„ ํƒ: Java / Spring Boot์—์„œ ํŒŒ์ผ ์ €์žฅ ์œ„์น˜ ๊ฒฐ์ •

by chuyj15 2025. 11. 27.
728x90
๋ฐ˜์‘ํ˜•
SMALL

%23 ๋Ÿฐํƒ€์ž„ ๋””๋ ‰ํ„ฐ๋ฆฌ ์„ ํƒ: Java / Spring Boot์—์„œ ํŒŒ์ผ ์ €์žฅ ์œ„์น˜ ๊ฒฐ์ •

์š”์•ฝ

์ด ๋ฌธ์„œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ ํŒŒ์ผ์„ ์ €์žฅํ•  ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” "๊ธฐ๋ณธ ๋””๋ ‰ํ„ฐ๋ฆฌ(base directory)"๋ฅผ ์„ ํƒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ: logs์™€ ๊ฐ™์€ ๋Ÿฐํƒ€์ž„ ํด๋” ์˜†์— web-client ํด๋”๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋‹ค๋ฃจ๋ฉฐ, IDE/ํŒจํ‚ค์ง€(JAR)/์„œ๋น„์Šค ํ™˜๊ฒฝ๋ณ„ ์ฃผ์˜์ ๊ณผ Spring Boot์— ์ ํ•ฉํ•œ ๊ถŒ์žฅ ์ „๋žต์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ ์ •์˜

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋กœ์ปฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ํŒŒ์ผ์„ ์ €์žฅํ•ด์•ผ ํ•  ๋•Œ, ์–ด๋А ์œ„์น˜์— ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งŒ๋“ค์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค:

  • IDE์—์„œ ์‹คํ–‰ํ•˜๋ฉด ํด๋ž˜์Šค ํŒŒ์ผ์ด target/classes ์•„๋ž˜์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
  • JAR๋กœ ํŒจํ‚ค์ง•ํ•ด ์‹คํ–‰ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ JAR ๋‚ด๋ถ€์— ์žˆ๊ณ , ๋กœ๊ทธ ๋“ฑ ๋Ÿฐํƒ€์ž„ ์‚ฐ์ถœ๋ฌผ์€ ๋ณดํ†ต JAR ํŒŒ์ผ๊ณผ ๊ฐ™์€ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • systemd ๊ฐ™์€ ์„œ๋น„์Šค๋กœ ๋“ฑ๋กํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค์˜ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ(working directory)๊ฐ€ JAR ์œ„์น˜์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชฉํ‘œ: web-client ํด๋”๊ฐ€ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์œ„์น˜(์˜ˆ: ํŒจํ‚ค์ง€๋œ JAR ์˜†, logs์™€ ๋™์ผํ•œ ์œ„์น˜)์— ์ƒ์„ฑ๋˜๋„๋ก ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์Šต๋‹ˆ๋‹ค.

์„ ํƒ์ง€์™€ ์žฅ๋‹จ์ 

  1. System.getProperty("user.dir") (ํ”„๋กœ์„ธ์Šค ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ)
    • ์žฅ์ : ๊ฐ„๋‹จํ•˜๋ฉฐ ํ˜„์žฌ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.
    • ๋‹จ์ : ํ”„๋กœ์„ธ์Šค๋ฅผ ์–ด๋–ป๊ฒŒ ์‹œ์ž‘ํ–ˆ๋Š”์ง€์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์„œ๋น„์Šค ๋งค๋‹ˆ์ €๊ฐ€ ๋‹ค๋ฅธ ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด JAR ์œ„์น˜๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  2. ProtectionDomain / codeSource๋กœ JAR/ํด๋ž˜์Šค ์œ„์น˜ ๊ฒฐ์ •
    • ์˜ˆ: MyClass.class.getProtectionDomain().getCodeSource().getLocation()
    • ์žฅ์ : JAR๋กœ ์‹คํ–‰๋  ๋•Œ JAR ํŒŒ์ผ URL์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ JAR์˜ ๋ถ€๋ชจ ํด๋”๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‚ผ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋‹จ์ : IDE์—์„œ ์‹คํ–‰ํ•˜๋ฉด target/classes๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๊ณ , ๋ณด์•ˆ ๊ด€๋ฆฌ์ž๋‚˜ ํŠน์ˆ˜ํ•œ ํด๋ž˜์Šค๋กœ๋” ๊ตฌ์„ฑ์—์„œ null์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. Spring Boot์˜ ApplicationHome
    • org.springframework.boot.system.ApplicationHome์€ Spring Boot ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‹คํ–‰ ์œ„์น˜(๋˜๋Š” JAR ์œ„์น˜)๋ฅผ ์•ˆ์ •์ ์œผ๋กœ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.
    • ์žฅ์ : ์ด ๋ชฉ์ ์„ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ๊ณ  JAR/ํญ๋ฐœ๋œ(exploded) ํ˜•ํƒœ ๋ชจ๋‘๋ฅผ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    • ๋‹จ์ : Spring Boot ์˜์กด์„ฑ์ด ํ•„์š”ํ•˜๋ฉฐ, ๋น„-Spring ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  4. ์„ค์ • ๊ธฐ๋ฐ˜ ๊ฒฝ๋กœ (properties / environment)
    • ์žฅ์ : ๊ฐ€์žฅ ๋ช…์‹œ์ ์ด๊ณ  ์ œ์–ด ๊ฐ€๋Šฅํ•จ (์˜ˆ: app.base-dir=/opt/myapp/data). ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.
    • ๋‹จ์ : ์šด์˜ํŒ€์ด ๊ฐ’์„ ์„ค์ •ํ•ด์•ผ ํ•˜๋ฉฐ ๋กœ์ปฌ ๊ฐœ๋ฐœ์—์„œ๋Š” ๋ฒˆ๊ฑฐ๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  5. ClassLoader ๋ฆฌ์†Œ์Šค ์œ„์น˜ ๋˜๋Š” ServletContext
    • WAR ๋ฐฐํฌ ํ™˜๊ฒฝ์—์„œ๋Š” ServletContext.getRealPath()๊ฐ€ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์œผ๋‚˜ JAR ํ™˜๊ฒฝ์—์„œ๋Š” ์‹ ๋ขฐํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

๊ถŒ์žฅ ์ „๋žต

IDE์—์„œ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ JAR/์„œ๋น„์Šค๋กœ ํŒจํ‚ค์ง•ํ•ด ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ชจ๋‘ ๊ณ ๋ คํ•  ๋•Œ, ๋‹ค์Œ ๊ณ„์ธตํ™”๋œ ์ ‘๊ทผ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค:

  1. ์„ค์ • ๊ฐ’(app.base-dir)์„ ์šฐ์„  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค(์šฐ์„ ์ˆœ์œ„ ๋†’์Œ). ์˜ˆ: application.properties ๋˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ APP_BASE_DIR.
  2. ์„ค์ •์ด ์—†์œผ๋ฉด Spring Boot๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ ๊ฒฝ์šฐ ApplicationHome์„ ์‚ฌ์šฉํ•ด JAR ๋˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์œ„์น˜ํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ฐพ์•„ ๊ทธ ์•„๋ž˜ web-client ๊ฐ™์€ ํ•˜์œ„ ํด๋”๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  3. ApplicationHome์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ฑฐ๋‚˜ ์‹คํŒจํ•˜๋ฉด ProtectionDomain/codeSource๋ฅผ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.
  4. ์œ„ ๋ฐฉ๋ฒ•๋“ค์ด ๋ชจ๋‘ ์‹คํŒจํ•˜๋ฉด user.dir๋กœ ํด๋ฐฑํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฐฉ์‹์€ ์„œ๋น„์Šค ํ™˜๊ฒฝ์—์„œ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๋™์ž‘์„ ์ œ๊ณตํ•˜๋ฉด์„œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ์˜ ํŽธ์˜์„ฑ๋„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

๊ตฌํ˜„ ์ฃผ์˜์‚ฌํ•ญ (Java / Spring Boot)

  • ๋””๋ ‰ํ„ฐ๋ฆฌ ์ƒ์„ฑ์—๋Š” ํ•ญ์ƒ java.nio.file.Path์™€ Files.createDirectories(...)๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
  • ๊ฒฝ๋กœ ๊ตฌ๋ถ„์ž๋Š” ํ•˜๋“œ์ฝ”๋”ฉ๋œ /๋‚˜ \\ ๋Œ€์‹  File.separator ๋˜๋Š” Path API๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
  • IOException ๋“ฑ์„ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•˜๊ณ  ์–ด๋–ค ํด๋ฐฑ์„ ์‚ฌ์šฉํ–ˆ๋Š”์ง€ ๋กœ๊ทธ๋กœ ๋‚จ๊ธฐ์„ธ์š”.
  • ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์„ค์ •์œผ๋กœ ๋ฒ ์ด์Šค ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ ๊ตฌํ˜„ (Spring Boot ์นœํ™”์ )

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.io.File;
import org.springframework.boot.system.ApplicationHome;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public final class BaseDirResolver {
    private static final String DEFAULT_FOLDER = "web-client";

    public static Path resolveBaseDir(String configuredBaseDir) {
        if (configuredBaseDir != null && !configuredBaseDir.isBlank()) {
            return Paths.get(configuredBaseDir).toAbsolutePath();
        }

        // 1) ApplicationHome ์‹œ๋„ (Spring Boot)
        try {
            ApplicationHome home = new ApplicationHome(BaseDirResolver.class);
            File dir = home.getDir();
            if (dir != null) {
                return dir.toPath().resolve(DEFAULT_FOLDER).toAbsolutePath();
            }
        } catch (Throwable t) {
            log.debug("ApplicationHome ์‚ฌ์šฉ ๋ถˆ๊ฐ€, ํด๋ฐฑ ์ง„ํ–‰", t);
        }

        // 2) ProtectionDomain / codeSource ์‹œ๋„
        try {
            java.net.URL codeLocation = BaseDirResolver.class.getProtectionDomain().getCodeSource().getLocation();
            Path codePath = Paths.get(codeLocation.toURI());
            Path parent = codePath.getParent();
            if (parent != null) {
                return parent.resolve(DEFAULT_FOLDER).toAbsolutePath();
            }
        } catch (Throwable t) {
            log.debug("codeSource ์กฐํšŒ ์‹คํŒจ, ํด๋ฐฑ", t);
        }

        // 3) user.dir ํด๋ฐฑ
        return Paths.get(System.getProperty("user.dir")).resolve(DEFAULT_FOLDER).toAbsolutePath();
    }

    public static Path ensureBaseDir(Path baseDir) throws java.io.IOException {
        Files.createDirectories(baseDir);
        return baseDir;
    }
}

์‚ฌ์šฉ ๋ฐฉ๋ฒ• (Spring Boot ์„œ๋น„์Šค)

  • ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” application.properties ๋˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— app.base-dir๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ์„ค์ •์ด ์—†์œผ๋ฉด ์ด ๊ตฌํ˜„์€ JAR์ด ์œ„์น˜ํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ ์˜†์— web-client๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ๋ณดํ†ต logs๊ฐ€ ์ƒ์„ฑ๋˜๋Š” ์œ„์น˜์™€ ๋™์ผํ•ด์ง‘๋‹ˆ๋‹ค.

systemd / ์„œ๋น„์Šค ๊ณ ๋ ค์‚ฌํ•ญ

  • systemd ์œ ๋‹›์€ WorkingDirectory๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, user.dir์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ ์ด๋ฅผ ๋ช…์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • JAR ์œ„์น˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‚ผ์œผ๋ฉด WorkingDirectory ์„ค์ •๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ JAR์˜ ๋ถ€๋ชจ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ฐพ์•„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋” ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค.

์˜ˆ์‹œ systemd ์Šค๋‹ˆํŽซ:

[Service]
User=appuser
ExecStart=/usr/bin/java -jar /opt/myapp/myapp.jar
WorkingDirectory=/opt/myapp
Restart=on-failure

ํ…Œ์ŠคํŠธ ๋ฐ ๊ฒ€์ฆ

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ: ์„ธ ๊ฐ€์ง€(์„ค์ •, ApplicationHome, codeSource/user.dir) ๊ฒฝ๋กœ๋ฅผ ๋ชจ์˜(mock)ํ•˜๊ฑฐ๋‚˜ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ด ๋ฐ˜ํ™˜๊ฐ’์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ: ํŒจํ‚ค์ง•๋œ JAR์„ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•ด JAR ์˜†์— web-client๊ฐ€ ์ƒ์„ฑ๋˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ: ์‹ค์ œ ์„œ๋ฒ„์— ๋ฐฐํฌํ•ด systemd๋กœ ์‹œ์ž‘ ํ›„ logs์™€ web-client๊ฐ€ ๋™์ผ ์œ„์น˜์— ์ƒ์„ฑ๋˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ๋ฐ ๊ถŒํ•œ

  • ํ”„๋กœ์„ธ์Šค๊ฐ€ ์„ ํƒํ•œ ๋ฒ ์ด์Šค ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์“ฐ๊ธฐ ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.
  • ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ „์ฒด ๊ถŒํ•œ(๋ชจ๋‘ ์“ฐ๊ธฐ)์œผ๋กœ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋งˆ์„ธ์š”.
  • ์—…๋กœ๋“œ ํŒŒ์ผ๋ช…์€ ๊ฒฝ๋กœ ํƒ์ƒ‰(path traversal) ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๋„๋ก ๊ฒ€์ฆํ•˜์„ธ์š”.

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ / ๋ฐฐํฌ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • ์šด์˜ํŒ€์ด ๋ช…์‹œ์  ๊ฒฝ๋กœ๋ฅผ ์„ ํ˜ธํ•˜๋ฉด application-*.properties์— app.base-dir๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.
  • JAR ์œ„์น˜์— ์˜์กดํ•œ๋‹ค๋ฉด ์„œ๋น„์Šค ๋งค๋‹ˆ์ €๊ฐ€ JAR์„ ์˜๋„ํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋‘๋„๋ก ๋ฌธ์„œํ™”ํ•˜์„ธ์š”.
  • ์šด์˜์ž์šฉ ๋ฌธ์„œ์— ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒฐ์ • ๋ฐฉ์‹์„ ๋ช…์‹œํ•˜์„ธ์š”.

๊ฒฐ๋ก 

์„ค์ • ์šฐ์„  ์ ‘๊ทผ(๋จผ์ € app.base-dir ํ™•์ธ), ๊ทธ ๋‹ค์Œ ApplicationHome(๋˜๋Š” codeSource), ๋งˆ์ง€๋ง‰์œผ๋กœ user.dir ํด๋ฐฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ณ  ์‹ค๋ฌด์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ”„๋กœ๋•์…˜(JAR/์„œ๋น„์Šค)์—์„œ๋Š” ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๊ณ  ๊ฐœ๋ฐœ(IDE) ํ™˜๊ฒฝ์—์„œ๋Š” ํŽธ์˜์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


์ฐธ๊ณ : ์ด ๋…ธํŠธ๋Š” ์ตœ๊ทผ LocalFileUtils.getBaseDir ๋ณ€๊ฒฝ ์‚ฌํ•ญ๊ณผ ๋Ÿฐํƒ€์ž„ logs ์˜†์— web-client๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

728x90
๋ฐ˜์‘ํ˜•
LIST