다국어를 제공하는 방법으로는 크게 3가지를 생각해 볼 수 있다.

 

1. 서버에서 json을 받아서 사용하기

2. 클라이언트에서 json을 하드코딩하기

3. 웹 문서 사이트 API 사용하기

 

서버에서 JSON 가져오기

서버로부터 JSON 형식으로 데이터를 받아서 클라이언트에서 적용만 하는 방식이다. 서버에서 데이터가 관리되기 때문에 내용에 변경사항이 생겨도 클라이언트에서 따로 수정이 필요하지 않으며 여러 플랫폼에서 동일한 데이터를 사용할 수 있다.

 

언어별 스키마 속성 추가

Monster라는 테이블이 있을 때 다음과 같이 정의한다.

Monster Table
{
	'monsterId' : '1',
    'monsterNameKor' : '박코딩',
    'monsterNameEng' : 'Coding Bak',
}

 

하나의 테이블에서 지원할 언어만큼 속성을 추가해서 만든다. 단순한 구조로 데이터가 추가되어도 DB가 크게 복잡해지지 않기 때문에 확장성이나 유지, 보수 측면에서 유리하다. 다만 사용하는 데 있어서 클라이언트에서는 적용할 때 서비스할 언어에 맞춰 Kor, Eng를 구분해서 접근하거나 또는 API 호출에서 구분해서 데이터를 받을 수 있도록 서버단에서 작업을 해줄 필요가 있다.

 

언어별 테이블 분리

제공할 언어마다 테이블을 따로 분리하는 구조이다.

Moster_Kor Table
{
	'id' : '1',
    'name' : '박코딩'
}

Monster_Eng Table
{
	'id' : '1',
    'name' : 'Coding Bak'
}

 

클라이언트에서는 별도로 처리할 필요 없이 name으로 접근해서 사용할 수 있어 편리하지만 서버 입장에서는 DB의 구조가 복잡해지고 지원할 언어의 수만큼 테이블이 배로 늘어나기 때문에 유지 보수에 불리하다.

 

다중 언어 전용 테이블 구조

다국어가 적용될 모든 데이터에 대한 정보를 모아두고 사용할 방식에 맞게 데이터를 정리해서 보낸다.

Monster Table
{
	'monsterId' : '1',
    'monsterName_tId' : '1'
}

Language Table
{
	'tId' : '1',
    'eng' : 'Coding Bak',
    'kor' : '박코딩',
}

 

 

번역이 필요한 정보들을 하나의 테이블에 묶고 별도의 키값을 통해서 데이터를 연동시켜 사용하는 방식이다. 한 곳에서 일괄적으로 정보들을 관리할 수 있기 때문에 데이터가 수정되거나 추가될 때 쉽게 처리가 가능하다. 하지만 데이터를 연관 짓고 필요한 정보만 묶어서 보내야 하기 때문에 DB구조와 쿼리문이 복잡해질 수 있다.

 

서버에서 언어 데이터를 관리하는 방식에는 여러 방법들이 있지만 서버에 요청하고 응답을 기다려야 하기 때문에 데이터량과 위치에 따라서 지연시간에 대한 문제가 발생할 수 있다. 서버에서 보낼 데이터량이나 서비스할 지역이 여러 곳이라면 각 지역별 인터넷 환경과 서버로부터 거리 또한 고려해서 대응이 필요하다.

 

이런 문제를 해결하기 위한 방법들도 존재한다.

 

Edge Computing

사용자 또는 데이터 소소의 물리적인 위치나 그 근처에서 컴퓨팅을 수행하는 것으로 사용자가 단말 장치와 가까운 위치에서 컴퓨팅 서비스를 처리하면 더 빠르고 안정적인 서비스를 제공받을 수 있는 여러 위치에서 공통의 리소스 풀을 사용하여 데이터 연산 및 처리를 분산시키는 방법이다.

 

AWS Lambda, Cloudflare Workers 등에서 서비스를 사용할 수 있다.

 

SSG

Server Side Generation 또는 Static Site Generation

Next.js 같은 메타 프레임워크에서 사용할 수 있는 방식으로 빌듯이 데이터를 받아서 미리 언어별로 페이지를 분기처리하는 방식이다.  빌드할 때 서버로부터 요청을 보내고 데이터를 받아와서 파일들을 미리 생성해 놓기 때문에 지연시간이 발생하지 않게 하는 방식이다.

 

클라이언트에서 관리

언어 데이터를 클라이언트단에서 관리하는 방식이다. 별도의 서버 요청이 필요 없기 때문에 요청하고 응답을 대기하는 시간이 없다. 하지만 정보가 변경될 때마다 클라이언트에서 수정이 필요할 수 있다.

 

클라이언트 내부에서 하드코딩으로 직접 언어 데이터를 정리해서 사용하는 경우 데이터 수정 시 클라이언트의 수정이 필요하고 다른 플랫폼의 클라이언트도 있는 경우 모두 동일하게 수정해서 데이터를 일치시켜야 하는 번거로움이 있다.

 

일반적으로 json 형식으로 데이터 파일을 만들고 클라이언트에서는 파일을 로드하고 코드상에서 값들을 가져다 쓰는 방식을 사용한다. 

 

별도의 json 다국어 데이터 파일
{
  "en": {
    "greeting": "Hello!",
    "buttonText": "Submit"
  },
  "ko": {
    "greeting": "안녕하세요!",
    "buttonText": "제출"
  },
  "fr": {
    "greeting": "Bonjour!",
    "buttonText": "Soumettre"
  }
}
using System;
using System.IO;
using Newtonsoft.Json.Linq;

public class Program
{
    public static void Main()
    {
        // JSON 파일 경로
        string jsonFilePath = "path/to/your/json/file.json";

        // JSON 파일 로드
        string jsonContent = File.ReadAllText(jsonFilePath);

        // JSON 파싱
        JObject jsonData = JObject.Parse(jsonContent);

        // 사용자 언어 설정 (예: "en", "ko", "fr")
        string userLanguage = "en";

        // 다국어 데이터 가져오기
        string greeting = jsonData[userLanguage]["greeting"].ToString();
        string buttonText = jsonData[userLanguage]["buttonText"].ToString();

        // 다국어 데이터 사용 예시
        Console.WriteLine(greeting);       // 인사말 출력
        Console.WriteLine(buttonText);     // 버튼 텍스트 출력
    }
}

파일을 별도로 두고 클라이언트에서는 파일을 로드하고 데이터를 가공해서 사용하게 된다. 이때 파일 위치를 번들처럼 외부에 두고 사용하여 언어 정보만 변경이 된 경우에는 해당 파일만 수정해 주면 클라이언트상에서 수정 없이 작업이 가능하다.

 

문서 관리 서비스 사용

여기서 문서 관리 서비스란 구글 스프레드시트 처럼 제공되는 서비스를 말한다. 직관적인 형식으로 데이터를 관리할 수 있으며 여러 클라이언트에서 동일한 데이터를 사용할 수 있다. 하지만 접근하기 위해서는 API의 호출이 필요하고 이 과정에서 서버에 요청이 필요하기 때문에 지연시간이 발생할 수밖에 없으며 서비스마다 지원되는 API에 제한이 있다.

 

using Google.Apis.Auth.OAuth2;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;
using System;
using System.Collections.Generic;
using System.IO;

public class Program
{
    static readonly string spreadsheetId = "your-spreadsheet-id";
    static readonly string credentialsFilePath = "path/to/your/credentials.json";

    public static void Main()
    {
        // 인증 정보 로드
        GoogleCredential credential;
        using (var stream = new FileStream(credentialsFilePath, FileMode.Open, FileAccess.Read))
        {
            credential = GoogleCredential.FromStream(stream)
                .CreateScoped(SheetsService.Scope.Spreadsheets);
        }

        // SheetsService 생성
        var serviceInitializer = new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = "Your Application Name"
        };
        var sheetsService = new SheetsService(serviceInitializer);

        // 데이터 가져오기
        IList<IList<object>> values = GetSpreadsheetValues(sheetsService, spreadsheetId, "Sheet1");

        // 가져온 데이터 출력
        if (values != null && values.Count > 0)
        {
            foreach (var row in values)
            {
                foreach (var cell in row)
                {
                    Console.Write(cell + "\t");
                }
                Console.WriteLine();
            }
        }
        else
        {
            Console.WriteLine("No data found.");
        }
    }

    public static IList<IList<object>> GetSpreadsheetValues(SheetsService sheetsService, string spreadsheetId, string sheetName)
    {
        string range = $"{sheetName}!A1:Z";
        SpreadsheetsResource.ValuesResource.GetRequest request =
            sheetsService.Spreadsheets.Values.Get(spreadsheetId, range);

        ValueRange response = request.Execute();
        IList<IList<object>> values = response.Values;

        return values;
    }
}

 

다양한 방법들로 다국어 데이터를 관리할 수 있는데 이 중 장단점들을 고려하여 프로젝트의 성격 및 특징에 알맞은 방식을 선택하는 것이 중요하다. 

728x90
반응형

'Memo' 카테고리의 다른 글

C# Class 크기 확인  (0) 2023.02.06

Visual Studio 2022

 

class 생성

멤버변수 int 하나

public class Test
{
	int data_1;
}

public static void Main(string[] args)
{
	Test test = new Test();
}

C# Class Size Test (Field int 1)

int 하나인데 크기가 24 바이트이다.

int = 4byte 인데 왜 6배나 되는걸까?

=>6개 이상일 때 크기 확인 해보기로함

public class Test
{
	public int data_1;
	public int data_2;
	public int data_3;
	public int data_4;
	public int data_5;
	public int data_6;
}

일단 int 6개로 테스트, 기대 결과는 그대로 24byte

C# Class Size Test (Field int 6)

이게 무슨일이지, 40 byte 가 나옴

처음 결과를 기준으로 생각해보면 24 + 16 즉 16 byre가 추가됨.

4(int) * 4(count) 라고 생각해보고 다시 필드를 int * 3으로 테스트

 

public class Test
{
    public int data_1;
    public int data_2;
    //public int data_3;
    //public int data_4;
    //public int data_5;
    //public int data_6;
}

예상한대로면 처음 결과인 24가 나와야함

C# Class Size Test (Field int 2)

결과는 기대한대로 나왔지만 여전히 24라는 크기가 이해가 안감,

혹시나해서 클래스를 인스턴스화 안했을때 한번 확인해봤지만 당연하게 해당 메모리 안잡힘

 

이번엔 하나씩 필드 늘려가면서 확인해보기로함

C# Class Size Test (Field int 3)

int * 4 일때는 동일하게 32

C# Class Size Test (Field int 5)

int * 5 부터 40 으로 잡혔음

 

그럼 그냥 빈 클래스는 얼마로 잡히나 확인했는데

애초에 비어있는 클래스를 인스턴스하면 24 byte 할당됨

여기서 2개씩 int 추가될 때마다 8 byte가 늘어나는데 이 늘어나는 값은 4(int) * 2(count) 맞음

 

비어있는 클래스가 왜 24 byte의 힙을 차지하는지 파악해보면될듯함

일단 Program 외부에 Test_2 클래스, 내부에는 Test_1 선언 후 비교

public class Test_2 { }
public class Program
{
    public class Test_1 { }
    public static void Main(string[] args)
    {
        Test_1 test_1 = new Test_1();
        Test_2 test_2 = new Test_2();
    }
}

C# Class Size Test_1 / Test_2 (empty)

테스트 해본 결과 클래스를 할당하면 기본 크기가 24로 잡히고 이 안에 8byte 까지는 포함시킬 수 있는걸로 보인다.

즉 기본 24 = 16(사용됨) + 8(여유 공간) 이고 크기가 24 byte를 넘을 경우 8byte 단위로 확장되는걸로 파악됨

 

구글링 해본 결과 내가 원하는 정보를 찾을 수 있었다.

 

OF MEMORY AND STRING_Jon Sket

 

본래 글의 취지는 .Net에서 ASCII 문자열의 효율적인 표현에 관한것에 대한 답이다.

위 글에서는 x86과 x64를 모두 비교하고 있는데 이 글에서는 x64를 기준으로 정리한다.

 

그럼 이제 왜 0byte는 될수없는지 그리고 24byte 중 사용할 수 없는 16byte와 사용 가능한 8byte는 무엇 때문인지 정리한다.

 

우선 C#에서 모든 타입은 object 타입을 베이스로한다. 그리고 이 object 타입의 크기는 24byte이다.

이 24byte의 크기는 16byte의 오버헤드와 참조를 위한 8byte로 이루어진다. 

 

오버헤드란 처리를 하기 위해 들어가는 간접적인 처리 시간, 메모리 등을 말한다. 즉 C#의 object를 메모리에 유지시키는데 필요한 메모리 양이 16byte이며 .Net의 CLR은 데이터가 없는 object에서 작업할 수 없기 때문에 오버헤드만 가지고는 object를 사용할 수 없고 여유 공간이 필요하며 이 크기가 8byte이다. 따라서 선언될 수 있는 가장 작은 크기의 메모리는 24byte가 된다.

 

이 중 여유공간인 8byte는 실제 데이터를 저장할 수 있기 때문에 int가 2개 포함된 class역시 24byte였다. 이 8byte는 전체를 사용할 수 있는 공간이기 때문에 1byte로 8를 사용하는것도 가능하다. 그리고 int가 3개가 될때 24byte에서 8byte로 증가했는데 이것은 메모리의 경계인 패딩의 크기가 8byte였기 때문이다. 

 

class 역시 object 타입을 베이스로 하고 있고 빈 class를 인스턴스화 하는것은 object를 생성하는것과 마찬가지이기 때문에 빈 class가 24byte라는것도 이제 납득이 간다. 

 

오버헤드에 대한 내용

overhead - Andrew Hunter
overhead - Stack Overflow

 

왜 이렇게 많은 공간이 필요한지 또 이 공간은 어떤식으로 사용되는지 궁금한데 이 부분은 더 자세히 찾아봐야 될것같다.

 

정리

64bit 기준으로 

1. 비어있는 class 또한 하나의 object이다. 따라서 24byte 메모리를 차지한다.

2. 이 메모리는 16byte의 내부 데이터 참조형으로 사용되는 오버헤드와 8byte의 여유공간으로 구성된다.

3. 여유공간은 모두 사용이 가능하며 메모리는 8byte의 패딩을 가지고 있다.

 

728x90
반응형

'Memo' 카테고리의 다른 글

다국어를 제공하는 방법에 대해서  (0) 2023.05.10

+ Recent posts