Java 에서 상대 경로로 JSON 파일 읽는 방법

JavaBeginner
지금 연습하기

소개

현대 Java 개발에서 JSON(JavaScript Object Notation) 을 다루는 것은 기본적인 기술입니다. JSON 은 사람이 읽기 쉬운 형식과 기계가 쉽게 파싱할 수 있다는 장점 때문에 웹 애플리케이션 및 API 에서 사실상의 데이터 교환 표준입니다.

이 튜토리얼은 Java 애플리케이션에서 상대 경로로 JSON 파일을 읽고 파싱하는 포괄적인 가이드를 제공합니다. 필요한 종속성 (dependencies) 을 갖춘 Maven 프로젝트를 설정하고, JSON 파일을 생성 및 구조화하며, 상대 경로에서 이를 읽고 복잡한 중첩 데이터를 포함한 내용을 파싱하는 방법을 배우게 됩니다.

이 실습을 마치면 Java 프로젝트 내에서 JSON 데이터를 관리하는 데 능숙해질 것입니다. 이는 웹 서비스, API 또는 구성 파일을 다루는 모든 개발자에게 중요한 기술입니다.

프로젝트 설정 및 JSON 파일 생성

JSON 파일을 읽기 전에 프로젝트 환경을 설정하고 파일을 직접 생성해야 합니다. 이 실습에서는 Apache Maven 을 사용하여 프로젝트 종속성 (dependencies) 을 관리합니다. 초기 설정에서 이미 표준 Maven 프로젝트 구조가 생성되었습니다.

프로젝트 구조 이해

왼쪽의 WebIDE 파일 탐색기에서 ~/project 디렉토리 내에 다음과 같은 구조를 볼 수 있습니다.

  • pom.xml: Maven 의 Project Object Model 파일입니다. 프로젝트의 종속성 및 빌드 구성을 정의합니다.
  • src/main/java: Java 소스 코드를 위한 디렉토리입니다.
  • src/main/resources: 구성 파일 또는 데이터 파일과 같은 리소스 파일을 위한 디렉토리입니다.

pom.xml 파일은 Java 에서 JSON 작업을 위한 인기 있는 선택인 org.json 라이브러리를 포함하도록 미리 구성되었습니다.

JSON 데이터 파일 생성

이제 다음 단계에서 읽을 JSON 파일을 생성해 보겠습니다.

  1. WebIDE 파일 탐색기에서 src/main/resources 디렉토리로 이동합니다.
  2. resources 폴더를 마우스 오른쪽 버튼으로 클릭하고 "New File"을 선택합니다.
  3. 파일 이름을 data.json으로 지정합니다.
  4. 새로 생성된 data.json 파일을 열고 다음 내용을 추가합니다.
{
  "name": "John Doe",
  "age": 28,
  "email": "john.doe@example.com",
  "isEmployed": true,
  "address": {
    "street": "123 Main St",
    "city": "Anytown"
  },
  "skills": ["Java", "SQL", "JavaScript"]
}

이 JSON 파일은 사용자 프로필을 나타냅니다. 간단한 키 - 값 쌍 ("name": "John Doe"와 같은), 중첩된 객체 (address), 문자열 배열 (skills) 을 포함합니다. 내용을 붙여넣은 후 파일을 저장합니다.

이제 프로젝트 설정과 JSON 데이터 파일 생성을 성공적으로 완료했습니다. 다음 단계에서는 이 파일을 읽고 파싱하는 Java 코드를 작성할 것입니다.

간단한 JSON 파일 읽기 및 파싱

JSON 파일을 준비했으니, 이제 이를 읽는 Java 프로그램을 작성할 수 있습니다. 애플리케이션이 다양한 환경에서 이식 가능하도록 일반적인 방법인 상대 경로를 사용할 것입니다.

상대 경로 이해

상대 경로는 현재 작업 디렉토리 (current working directory) 를 기준으로 지정됩니다. 터미널에서 Maven 프로젝트를 실행할 때, 작업 디렉토리는 일반적으로 프로젝트의 루트 폴더이며, 이 경우 ~/project입니다. 따라서 JSON 파일에 대한 상대 경로는 src/main/resources/data.json입니다.

Java JSON Reader 생성

파일을 읽을 Java 클래스를 만들어 보겠습니다.

  1. WebIDE 파일 탐색기에서 src/main/java/com/labex로 이동합니다.
  2. com/labex 폴더를 마우스 오른쪽 버튼으로 클릭하고 "New File"을 선택합니다.
  3. 파일 이름을 JsonReader.java로 지정합니다.
  4. 파일을 열고 다음 Java 코드를 추가합니다.
package com.labex;

import org.json.JSONObject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class JsonReader {
    public static void main(String[] args) {
        try {
            // JSON 파일에 대한 상대 경로 정의
            String filePath = "src/main/resources/data.json";
            System.out.println("Reading file from: " + filePath);

            // 파일 내용을 문자열로 읽기
            String content = new String(Files.readAllBytes(Paths.get(filePath)));

            // 문자열 내용으로부터 JSONObject 생성
            JSONObject jsonObject = new JSONObject(content);

            // 간단한 키의 값에 접근 및 출력
            String name = jsonObject.getString("name");
            int age = jsonObject.getInt("age");
            boolean isEmployed = jsonObject.getBoolean("isEmployed");

            System.out.println("\n--- Parsed JSON Data ---");
            System.out.println("Name: " + name);
            System.out.println("Age: " + age);
            System.out.println("Is Employed: " + isEmployed);

        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("Error parsing JSON: " + e.getMessage());
        }
    }
}

코드 설명

  • package com.labex;: 표준 Java 규칙에 따라 이 클래스가 com.labex 패키지에 속함을 선언합니다.
  • String filePath = "src/main/resources/data.json";: 파일에 대한 상대 경로를 정의합니다.
  • Files.readAllBytes(Paths.get(filePath)): 파일의 전체 내용을 바이트 배열로 읽어온 후 문자열로 변환합니다.
  • new JSONObject(content): JSON 데이터가 포함된 문자열을 JSONObject로 파싱합니다.
  • jsonObject.getString("name"): 키 "name"과 연결된 문자열 값을 검색합니다. getInt()getBoolean()과 같은 유사한 메서드가 다른 데이터 유형에 사용됩니다.

프로그램 컴파일 및 실행

이제 Maven 을 사용하여 코드를 컴파일하고 실행해 보겠습니다.

  1. WebIDE 에서 터미널을 엽니다.
  2. 다음 명령을 실행하여 클래스를 컴파일하고 실행합니다.
mvn compile exec:java -Dexec.mainClass="com.labex.JsonReader"

터미널에서 다음과 같은 출력을 볼 수 있습니다.

Reading file from: src/main/resources/data.json

--- Parsed JSON Data ---
Name: John Doe
Age: 28
Is Employed: true

이 출력은 프로그램이 상대 경로에서 data.json 파일을 성공적으로 읽고 기본 속성을 파싱했음을 확인시켜 줍니다.

복잡한 JSON 데이터 처리

실제 JSON 데이터는 종종 중첩된 구조, 예를 들어 객체 안의 객체 또는 값의 배열을 포함합니다. data.json 파일에는 중첩된 address 객체와 skills 배열이 포함되어 있습니다. 이러한 복잡한 구조를 파싱하기 위해 지식을 확장해 보겠습니다.

복잡한 JSON 파서 생성

JSON 파일의 이러한 더 복잡한 부분을 파싱하는 것을 시연하기 위해 새 클래스를 만들 것입니다.

  1. WebIDE 파일 탐색기에서 src/main/java/com/labex로 이동합니다.
  2. ComplexJsonParser.java라는 새 파일을 생성합니다.
  3. 파일에 다음 코드를 추가합니다.
package com.labex;

import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ComplexJsonParser {
    public static void main(String[] args) {
        try {
            String filePath = "src/main/resources/data.json";
            String content = new String(Files.readAllBytes(Paths.get(filePath)));
            JSONObject person = new JSONObject(content);

            System.out.println("--- Parsing Complex JSON Data ---");

            // 중첩된 'address' 객체에 접근
            JSONObject address = person.getJSONObject("address");
            String street = address.getString("street");
            String city = address.getString("city");

            System.out.println("\nAddress:");
            System.out.println("  Street: " + street);
            System.out.println("  City: " + city);

            // 'skills' 배열에 접근
            JSONArray skills = person.getJSONArray("skills");
            System.out.println("\nSkills:");
            for (int i = 0; i < skills.length(); i++) {
                String skill = skills.getString(i);
                System.out.println("  - " + skill);
            }

        } catch (IOException e) {
            System.out.println("Error reading file: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("Error parsing JSON: " + e.getMessage());
        }
    }
}

코드 설명

  • person.getJSONObject("address"): 이 메서드는 "address" 키와 연결된 중첩된 JSON 객체를 검색합니다. 그런 다음 이 새 JSONObject에서 getString()과 같은 메서드를 호출할 수 있습니다.
  • person.getJSONArray("skills"): 이 메서드는 "skills" 키와 연결된 JSON 배열을 검색합니다.
  • skills.length(): JSONArray에 있는 요소의 수를 반환합니다.
  • skills.getString(i): JSONArray 내의 특정 인덱스 i에 있는 문자열 요소를 검색합니다.

파서 컴파일 및 실행

이 새 클래스를 실행하여 출력을 확인해 보겠습니다.

  1. WebIDE 에서 터미널을 엽니다.
  2. 다음 명령을 실행합니다.
mvn compile exec:java -Dexec.mainClass="com.labex.ComplexJsonParser"

예상되는 출력은 다음과 같습니다.

--- Parsing Complex JSON Data ---

Address:
  Street: 123 Main St
  City: Anytown

Skills:
  - Java
  - SQL
  - JavaScript

이는 JSON 구조 내의 중첩된 객체 및 배열을 탐색하고 데이터를 추출하는 능력을 보여줍니다. 이는 복잡한 데이터 피드 및 API 응답을 처리하는 데 중요한 기술입니다.

재사용 가능한 JSON 유틸리티 생성

소프트웨어 개발에서는 재사용 가능한 코드를 작성하는 것이 모범 사례입니다. 파일 읽기 로직을 필요한 모든 클래스에서 반복하는 대신 유틸리티 클래스를 만들 수 있습니다. "Don't Repeat Yourself" (DRY) 로 알려진 이 접근 방식은 코드를 더 깔끔하고 유지 관리하기 쉬우며 오류 발생 가능성을 줄여줍니다.

JsonUtils 클래스 생성

JSON 파일을 읽는 로직을 캡슐화하는 유틸리티 클래스를 만들어 보겠습니다.

  1. WebIDE 에서 src/main/java/com/labex 디렉토리 안에 JsonUtils.java라는 새 파일을 생성합니다.
  2. 다음 코드를 추가합니다.
package com.labex;

import org.json.JSONObject;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class JsonUtils {

    /**
     * 상대 경로에서 JSON 파일을 읽어 JSONObject 로 파싱합니다.
     *
     * @param filePath JSON 파일의 상대 경로.
     * @return 파일 내용을 나타내는 JSONObject.
     * @throws IOException 파일을 읽는 동안 I/O 오류가 발생하는 경우.
     */
    public static JSONObject readJsonObjectFromFile(String filePath) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filePath)));
        return new JSONObject(content);
    }
}

이 클래스에는 단일 static 메서드인 readJsonObjectFromFile이 포함되어 있습니다. static 메서드는 인스턴스가 아닌 클래트 자체에 속하므로 객체를 생성하지 않고 클래스 이름을 사용하여 직접 호출할 수 있습니다 (예: JsonUtils.readJsonObjectFromFile(...)).

유틸리티 클래스 사용

이제 새 유틸리티를 사용하는 메인 애플리케이션 클래스를 만들어 보겠습니다.

  1. WebIDE 에서 src/main/java/com/labex 디렉토리 안에 Main.java라는 새 파일을 생성합니다.
  2. Main.java에 다음 코드를 추가합니다.
package com.labex;

import org.json.JSONObject;

public class Main {
    public static void main(String[] args) {
        try {
            // 유틸리티 클래스를 사용하여 JSON 파일 읽기
            JSONObject data = JsonUtils.readJsonObjectFromFile("src/main/resources/data.json");

            // 이제 이전과 같이 JSONObject 를 사용할 수 있습니다.
            String name = data.getString("name");
            String city = data.getJSONObject("address").getString("city");

            System.out.println("--- JsonUtils 를 사용하여 읽은 데이터 ---");
            System.out.println("Name: " + name);
            System.out.println("City: " + city);
            System.out.println("\nJsonUtils 를 사용하여 JSON 을 성공적으로 읽었습니다.");

        } catch (Exception e) {
            System.out.println("오류가 발생했습니다: " + e.getMessage());
        }
    }
}

Main 클래스는 훨씬 더 깔끔합니다. 파일 읽기 및 파싱 작업을 JsonUtils에 위임하고 결과 JSONObject로 무엇을 할지에만 집중합니다.

메인 애플리케이션 실행

마지막으로 Main 클래스를 실행해 보겠습니다.

  1. WebIDE 에서 터미널을 엽니다.
  2. 다음 명령을 실행합니다.
mvn compile exec:java -Dexec.mainClass="com.labex.Main"

다음과 같은 출력을 볼 수 있습니다.

--- Data read using JsonUtils ---
Name: John Doe
City: Anytown

Successfully read JSON using JsonUtils.

유틸리티 클래스를 생성함으로써 코드를 더 모듈화하고 재사용 가능하게 만들었습니다. 이는 효과적인 소프트웨어 엔지니어링의 핵심 원칙입니다.

요약

이 실습에서는 Java 에서 JSON 파일을 처리하는 실질적인 기술을 습득했습니다. 기본 설정부터 고급 파싱 기법까지 진행했으며, 재사용 가능한 코드를 만드는 것으로 마무리했습니다.

학습한 내용을 요약하면 다음과 같습니다.

  • 프로젝트 설정: Maven 프로젝트를 구조화하고 JSON 작업을 위해 org.json과 같은 외부 라이브러리를 포함하는 방법을 배웠습니다.
  • JSON 파일 생성: 문자열, 숫자, 부울, 중첩 객체 및 배열을 포함한 다양한 데이터 유형을 포함하는 잘 구성된 JSON 파일을 생성했습니다.
  • 상대 경로에서 읽기: 애플리케이션을 더 이식성 있게 만드는 기술인 상대 경로를 사용하여 파일을 성공적으로 읽었습니다.
  • JSON 파싱: org.json 라이브러리를 사용하여 JSON 문자열을 JSONObjectJSONArray 객체로 파싱하여 프로그래밍 방식으로 데이터에 액세스할 수 있었습니다.
  • 복잡한 구조 처리: 중첩된 JSON 객체를 탐색하고 JSON 배열을 반복하여 복잡한 데이터를 추출하는 방법을 배웠습니다.
  • 코드 재사용성: 파일 읽기 로직을 캡슐화하는 JsonUtils 클래스를 만들어 DRY(Don't Repeat Yourself) 원칙을 적용하여 더 깔끔하고 유지 관리하기 쉬운 코드를 만들었습니다.

JSON 데이터를 읽고, 파싱하고, 조작하는 능력은 웹 서비스, API 통합 또는 애플리케이션 구성에 관련된 모든 Java 개발자에게 필수적입니다. 이 실습에서 구축한 기반은 더 복잡한 개발 과제를 해결하는 데 매우 유용할 것입니다.