๐Ÿ’พ์œ ๋‹ˆํ‹ฐ - JSON ํŒŒ์ผ๋กœ ๊ฒŒ์ž„ ์ €์žฅํ•˜๊ธฐ 1-2 (์ €์žฅ ๊ธฐ๋Šฅ ๋งŒ๋“ค์–ด๋ณด๊ธฐ)

JP Kimยท2024๋…„ 11์›” 25์ผ

์œ ๋‹ˆํŒ…

๋ชฉ๋ก ๋ณด๊ธฐ
4/5

์ง€๋‚œ ๊ธ€์—์„œ JSON ํ˜•์‹์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜๋‹ค.
์ด๋ฒˆ์—๋Š” ๋ณธ๊ฒฉ์ ์œผ๋กœ JSON ํŒŒ์ผ์„ ์ด์šฉํ•ด์„œ ๊ฒŒ์ž„ ์ €์žฅ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณด์ž. ์ฝ”๋“œ๋งŒ ์ฐธ๊ณ ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ๋งจ ๋ฐ‘์œผ๋กœ ๋‚ด๋ฆฌ๋ฉด ๋œ๋‹ค.


1. JSON์œผ๋กœ ์ €์žฅํ•  ๋ฐ์ดํ„ฐ๋Š”?

์ผ๋‹จ ์ €์žฅ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์—, ์šฐ๋ฆฌ๋Š” ๋ฌด์Šจ ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ์ €์žฅํ•ด์•ผํ• ๊นŒ? ๋ฐ์ดํ„ฐ์•ผ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ ๋ณดํ†ต ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ˆ์‹œ๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

ํ”Œ๋ ˆ์ด์–ด์˜ ์ •๋ณด, ๊ฒŒ์ž„ ์„ค์ •, ํ˜„์žฌ๊นŒ์ง€์˜ ์ง„ํ–‰ ์ƒํ™ฉ...

์ด์ œ ์šฐ๋ฆฐ ์ด๋Ÿฐ ๋ฐ์ดํ„ฐ๋“ค์„ JSON ํ˜•์‹์œผ๋กœ ์ €์žฅํ•ด์•ผํ•œ๋‹ค. ์•ž์„  ๊ธ€์—์„œ ๋‚˜์™”๋˜ ๊ฒƒ์ฒ˜๋Ÿผ, JSON ํŒŒ์ผ์€ ํ‚ค์™€ ๊ฐ’ ์Œ์˜ ํ˜•ํƒœ๋กœ ์ด๋ค„์ ธ ์žˆ๋‹ค๋Š”๊ฑธ ๊ธฐ์–ตํ•˜์ž. ์œ„์˜ ์˜ˆ์‹œ์—์„œ ์–ธ๊ธ‰๋œ ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ๋‚˜ํƒ€๋‚ธ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

{
  "player": {
    "name": "Player1",
    "level": 5,
    "items": ["sword", "shield"],
    "currentStage": {
      "id": 3,
      "name": "Forest of Shadows",
      "progress": 45
    },
    "clearedStages": [
      {
        "id": 1,
        "name": "Plains of Beginning",
        "time": "00:12:34"
      },
      {
        "id": 2,
        "name": "Cave of Echoes",
        "time": "00:24:56"
      }
    ]
  },
  "settings": {
    "volume": 70,
    "difficulty": "normal"
  }
}

์ด์™ธ์—๋„ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋“ค์ด ํฌํ•จ๋  ์ˆ˜๋„ ์žˆ๋‹ค.


2. JSON์œผ๋กœ ๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๊ธฐ

์ž, ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ JSON ํ˜•์‹์œผ๋กœ ์ €์žฅํ• ์ง€๋„ ๊ณ ๋ฏผํ•ด๋ดค์œผ๋‹ˆ ์ด์ œ JSON์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด๋ณด์ž.

๋จผ์ € ์šฐ๋ฆฌ๋Š” ํŒŒ์ผ์„ ์ €์žฅํ•  ์œ„์น˜๋ฅผ ์ •ํ•ด์•ผํ•œ๋‹ค. ํŒŒ์ผ์„ ์•„๋ฌด๋Ÿฐ ์œ„์น˜์—๋‹ค ์ €์žฅํ•ด๋†“์œผ๋ฉด ๋‚˜์ค‘์— ์ฐพ๊ธฐ๋„ ํž˜๋“ค๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ๋„ ๋ฒ„๊ฒ๋‹ค.

์ด๋Ÿด ๋•Œ๋ฅผ ์œ„ํ•ด ์œ ๋‹ˆํ‹ฐ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ๋กœ์ธ Application.persistentDataPath ๊ฐ€ ์žˆ๋‹ค.

์ด ํ”„๋กœํผํ‹ฐ๋Š” ๊ฐ ํ”Œ๋žซํผ ๋ณ„๋กœ ์ ์ ˆํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๊ธฐ์— ๋ฒˆ๊ฑฐ๋กญ๊ฒŒ ๊ฒฝ๋กœ๋ฅผ ์ผ์ผํžˆ ์ •ํ•ด์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค.

  • Windows: C:\Users\<์‚ฌ์šฉ์ž์ด๋ฆ„>\AppData\LocalLow\<ํšŒ์‚ฌ์ด๋ฆ„>\<ํ”„๋กœ์ ํŠธ์ด๋ฆ„>
  • macOS: ~/Library/Application Support/<ํšŒ์‚ฌ์ด๋ฆ„>/<ํ”„๋กœ์ ํŠธ์ด๋ฆ„>
  • Android: /data/data/<ํŒจํ‚ค์ง€์ด๋ฆ„>/files
  • iOS: <์•ฑ ์ƒŒ๋“œ๋ฐ•์Šค ๋””๋ ‰ํ† ๋ฆฌ>/Documents

๋งŒ์•ฝ ์‹ค์ œ ๊ฒฝ๋กœ๊ฐ€ ๋งž๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉด ๋œ๋‹ค.

Debug.Log("Save Path: " + Application.persistentDataPath);

๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์„ ์‚ญ์ œํ•˜์ง€ ์•Š๋Š” ํ•œ ๋ฐ์ดํ„ฐ๋Š” ์˜๊ตฌ์ ์œผ๋กœ ์œ ์ง€๋˜๊ธฐ์— ์‚ฌ์šฉ์ž์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋‹ค.

๋‹ค๋งŒ ํŒŒ์ผ ๋ณ€์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ธฐ์—, ์ค‘์š”ํ•˜๊ฑฐ๋‚˜ ๊ฐœ์ธ์ ์ธ ์ •๋ณด๋ฅผ ์•”ํ˜ธํ™”ํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ Application.persistentDataPath ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ์ถ”์ฒœ๋˜์ง€ ์•Š๋Š”๋‹ค.

๐Ÿค” dataPath? // streamingAssetsPath?

Application ํด๋ž˜์Šค์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ดํŽด๋ณด๋ฉด, ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

์ด์ค‘์—์„œ ๋ช‡ ๊ฐ€์ง€๋งŒ ์‚ดํŽด๋ณด๋ฉด


  • Application.dataPath
    • ์—๋””ํ„ฐ ์ƒ์—์„  ์œ ๋‹ˆํ‹ฐ ํ”„๋กœ์ ํŠธ์˜ Assets ํด๋” ๊ฒฝ๋กœ๋ฅผ,
      ๋นŒ๋“œ๋œ ์•ฑ์—์„  ์•ฑ์˜ Data ํด๋” ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜.
    • ์ฝ๊ธฐ ์ „์šฉ
    • ์ด ํ”„๋กœํผํ‹ฐ๋ฅผ ํ†ตํ•ด ๊ฒŒ์ž„ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋ฉด ์•ˆ๋จ.

Asset ํด๋” ์•ˆ์˜ ๋‚ด์šฉ์€ ๋นŒ๋“œ ์‹œ, ํŠน์ • ํ˜•์‹์œผ๋กœ ํŒจํ‚ค์ง• ๋˜๊ธฐ์— ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
๋‹ค๋งŒ Resources ํด๋”์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ์•„๋ž˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜์ด ์•„๋‹Œ Resources.Load ๋ฅผ ์‚ฌ์šฉํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๋ฐ‘์—์„œ ์–˜๊ธฐํ•  StreamingAssets ํด๋”์˜ ๊ฒฝ์šฐ์—๋งŒ ์˜ˆ์™ธ์ ์œผ๋กœ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

// Resources/myFile.txt ํŒŒ์ผ์„ ์ฝ๊ธฐ
TextAsset textAsset = Resources.Load<TextAsset>("myFile"); // ํ™•์žฅ์ž ์ œ์™ธ

  • Application.streamingAssetsPath
    • StreamingAssets ํด๋”์˜ ๊ฒฝ๋กœ ๋ฐ˜ํ™˜
    • StreamingAssets ํด๋”๋Š” ํ•ด๋‹น ๋‚ด์šฉ ๊ทธ๋Œ€๋กœ ํŒจํ‚ค์ง• ๋˜๊ธฐ์— ๋นŒ๋“œ ํ›„์—๋„ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅ
    • ์ฝ๊ธฐ ์ „์šฉ

์ด ์™ธ์—๋„ ์ž„์‹œ์ ์ธ ์บ์‹œ ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๊ฒฝ๋กœ์ธ, Application.temporaryCachePath

์ฝ˜์†” ๋กœ๊ทธ์— ๋Œ€ํ•œ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”,
Application.consoleLogPath ๊ฐ€ ์žˆ๋‹ค.

3. JSON ์ง๋ ฌํ™”(Serialization)

์ž ์ด์ œ ๊ฒŒ์ž„ ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•ด๋ณด์ž. UNITY๋Š” ์ด๋Ÿฐ ์ง๋ ฌํ™” ๊ณผ์ •์„ ์œ„ํ•ด JsonUtility๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋ฉฐ, ์ด ์™ธ์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ Newtonsoft์˜ JSON ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์œ ๋‹ˆํ‹ฐ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” JsonUtility๋ฅผ ํ†ตํ•ด์„œ ์ง๋ ฌํ™”๋ฅผ ํ•ด๋ณด์ž.

๋จผ์ € ๊ฒŒ์ž„ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด์ฃผ๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์ž.

[System.Serializable] // ์ง๋ ฌํ™”๋ฅผ ํ•˜๊ฒ ๋‹ค๋Š” ์–ดํŠธ๋ฆฌ๋ทฐํŠธ(Attribute)
public class PlayerData
{
    public string name;
    public int level;
    public string[] items;
}

์ด์ œ SaveManager ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ์–ด์ฃผ์ž.

using System.IO;
using UnityEngine;

public class SaveManager : MonoBehaviour
{
    public void SaveData(PlayerData data)
    {
        // ๊ฒฝ๋กœ ์„ค์ •
        string path = Path.Combine(Application.persistentDataPath, "playerData.json");

        // JSON์œผ๋กœ ์ง๋ ฌํ™”
        string jsonData = JsonUtility.ToJson(data, true); // true๋Š” ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ํฌ๋งทํŒ…
        Debug.Log("JSON Data : \n" + jsonData);

        // ํŒŒ์ผ๋กœ ์ €์žฅ
        File.WriteAllText(path, jsonData);

        Debug.Log("Data saved to " + path);
    }
}
  • Path.Combine : ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋ฌธ์ž์—ด์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๊ณ  ์˜ฌ๋ฐ”๋ฅธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜.
  • JsonUtility.ToJson : ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ง๋ ฌํ™”ํ•  ๊ฐ์ฒด๋ฅผ ๋ฐ›๊ณ , ๋งŒ์•ฝ ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์— true ๋„ฃ๋Š”๋‹ค๋ฉด ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋ณ€ํ™˜.
  • File.WriteAllText : ํ•ด๋‹น ๊ฒฝ๋กœ์— ํ…์ŠคํŠธ ํŒŒ์ผ์„ ์ €์žฅ.

์ด์ œ ์•„๋ž˜์˜ ์˜ˆ์ œ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๊ณ  ๋ฐฉ๊ธˆ ์–˜๊ธฐํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ํŒŒ์ผ์ด ์ƒ์„ฑ๋œ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestJson : MonoBehaviour
{
    void Start()
    {
        PlayerData player = new PlayerData // ๋ฐ์ดํ„ฐ ๊ฐ์ฒด ์ƒ์„ฑ
        {
            name = "Player1",
            level = 5,
            items = new string[] { "sword", "shield" }
        };

        SaveManager saveManager = new SaveManager(); // SaveManager ๊ฐ์ฒด ์ƒ์„ฑ
        saveManager.SaveData(player); // ์„ธ์ด๋ธŒ ์‹œ์ž‘
    }
}

JSON ํŒŒ์ผ๋กœ ์ง๋ ฌํ™” ๋œ ๋ชจ์Šต

ํŒŒ์ผ์ด ์ €์žฅ๋œ ์œ„์น˜


์ €์žฅ๋œ ํŒŒ์ผ๊ณผ ๋‚ด์šฉ


4. ์˜ค๋ฅ˜ ์ผ€์ด์Šค ์ฒ˜๋ฆฌํ•˜๊ธฐ

์ด๊ฑธ๋กœ ์ €์žฅ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋‹ค! ํ•˜์ง€๋งŒ ์ด ์ฝ”๋“œ๋Š” ํŒŒ์ผ ์ €์žฅ ๊ณผ์ •์—์„œ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•œ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๋กœ์ง๊นŒ์ง€ ์งœ๋ณด์ž.


  1. ํŒŒ์ผ ๊ฒฝ๋กœ ์ฒดํฌ
    • ๋งŒ์•ฝ ์ €์žฅํ•  ๊ฒฝ๋กœ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด, ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์ฒดํฌํ•˜๊ณ  ๋งŒ์•ฝ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
if (!Directory.Exists(Application.persistentDataPath))
{
    Directory.CreateDirectory(Application.persistentDataPath);
}
  • Directory.CreateDirectory : ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„ฃ์€ ๊ฒฝ๋กœ๋ฅผ ์ƒ์„ฑ

  1. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
    • try-catch ๋ฌธ์œผ๋กœ ์ €์žฅ ๊ณผ์ • ์ค‘์— ์˜ˆ์ƒ์น˜ ๋ชปํ•˜๊ฒŒ ์ƒ๊ธฐ๋Š” ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
try
{
    File.WriteAllText(path, jsonData);
    Debug.Log("Data saved successfully.");
}
catch (Exception e)
{
    Debug.LogError("Failed to save data: " + e.Message);
}

์ด ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํฌํ•จํ•œ ์ „์ฒด์ ์ธ ์ €์žฅ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

public class SaveManager : MonoBehaviour
{
    public void SaveData(PlayerData data)
    {
    	// ์ €์žฅ ๊ฒฝ๋กœ ์„ค์ •
        string path = Path.Combine(Application.persistentDataPath, "playerData.json");

        try
        {
            // JSON ์ง๋ ฌํ™”
            string jsonData = JsonUtility.ToJson(data, true);
        	Debug.Log("JSON Data : \n" + jsonData);
        
            // ๊ฒฝ๋กœ ํ™•์ธ ๋ฐ ์ƒ์„ฑ
            if (!Directory.Exists(Application.persistentDataPath))
            {
                Directory.CreateDirectory(Application.persistentDataPath);
            }

            // JSON ๋ฐ์ดํ„ฐ ์ €์žฅ
            File.WriteAllText(path, jsonData);
            Debug.Log("Data saved to " + path);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to save data: " + e.Message);
        }
    }
}

์ด๋ ‡๊ฒŒ ํ•ด์„œ ์ €์žฅ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ดค๋‹ค.
๋‹ค์Œ ๊ธ€์—์„  ์ด ์ €์žฅํ•œ ํŒŒ์ผ์„ ์–ด๋–ป๊ฒŒ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž.

profile
๋‹น์‹ ์„ ํ•œ ์ค„๋กœ ์†Œ๊ฐœํ•ด๋ณด์„ธ์š”

1๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2024๋…„ 11์›” 25์ผ

์ €์žฅ์— ๋Œ€ํ•ด ์ƒ์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•ด์ฃผ์…จ๋„ค์š”! ์ข‹์€ ์ •๋ณด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ