NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C# .NET 매크로 프로그램 만들기. (변수와 플레이스홀더 텍스트)

    페이지 정보

    본문

    안녕하세요. 엔지엠소프트웨어입니다. 오늘은 매크로 프로그램을 제작할 때 변수 처리를 좀 더 쉽게 사용할 수 있도록 해주는 플레이스홀더(Placeholder)에 대해 알아보도록 하겠습니다. 이 기능은 엔지엠 매크로 6에 이미 포함되어 있는 기능인데요. 엔지엠 7 버전의 플레이스홀더는 최적화와 퍼포먼스가 상당부분 개선되었습니다. 플레이스홀더는 특정 브래킷(Bracket)을 이용해서 문자열 내에서 홀더 역할을 하고, 홀더 키와 매칭되는 변수를 플레이스합니다.

    ※ 브래킷은 중괄호를 사용합니다.

     

    위의 기본적인 설명이 약간 복잡하게 느껴질수도 있습니다. 아래 예와 같이 동작한다고 생각하시면 됩니다.

    • Hello {name}, Welcome to {product}
    • Hello John, Welcome to NGMsoftware!

     

    플레이스홀더는 액션이 아닙니다. 따라서, 모든 액션에서 사용할 수 있는 글로벌 메소드로 구현해야 합니다. 플레이스할 텍스트는 패턴 인자로 넘겨줍니다. 이외의 인자들은 부가적인 기능으로 플레이어와 플레이스홀더가 적용될 속성명입니다.

    public static string? GetMatches(IPlayer player, PropertyInfo property, string? pattern)

     

    토큰 목록은 글로벌 변수, 패러언트 변수, 로컬 변수를 구분하는 키워드와 플레이스홀더의 내부 키를 튜플로 저장합니다. 그리고, matches 맴버 변수는 플레이스홀더 키와 변수 목록의 매치 여부에 따라서 반환할 결과를 만드는데 사용됩니다. 물론, 내부에서 모든 처리를 수행해야 한다면 matches는 필요하지 않습니다. 별도의 반환 값이 필요하고, 반환 값에 따라서 로직이 필요하다면 이 내용을 변경해서 사용해야 합니다.

    var tokens = new List<Tuple<string, string>>();
    var matches = new Dictionary<string, string>();

     

    토큰 목록은 아래와 같이 만듭니다. 정규식을 이용해서 브래킷을 찾고, 브래킷 안의 키를 토큰 목록에 저장합니다. 여기서 G, P, L 키는 Global, Parent, Local의 약자입니다.

    pattern = Regex.Replace(pattern, @"\\{.*?}", (match) =>
    {
        var name = match.Value.Substring(2, match.Value.Length - 3);
        tokens.Add(new Tuple<string, string>("L", name));
        return $"(?<{name}>.*)";
    });

     

    엔지엠 매크로는 다양한 방식으로 변수를 처리할 수 있는데요. Global은 별도의 스크립트 또는 함수가 변수의 값을 공유하기 위해서 사용합니다. Parent는 Global 변수보다 한단계 낮은 레벨의 변수입니다. 말그대로 서브 스크립트가 자신이 속한 부모의 변수에만 데이터를 조작할 수 있습니다. 마지막으로 Local 변수는 자신의 스크립트 안에서만 데이터를 저장하거나 가져올 수 있습니다.

     

    이렇게 접근할 수 있는 영역을 구분해두는 이유는 변수 아이디의 중복 문제를 해소하기 위함입니다. 예를 들어서 A 변수와 A 변수가 있다고 생각 해보세요. 둘다 동일한 이름이기 때문에 변수를 가져올 때 어떤 변수에 저장된 값을 가져와야 하는지 알 수 없습니다. 반대로 어떤 변수에 데이터를 저장해야 하는지도 알 수 없게됩니다. 규모가 큰 스크립트를 제작할 때 변수 이름을 중복되지 않게 만드는게 중요하지만, 분리된 스크립트에서는 변수 이름을 동일하게 사용해도 문제가 되지 않습니다.

     

    Start1이라는 스크립트에 a라는 변수가 있고, Script2에도 a라는 변수가 있다면 이는 큰 문제가 되지 않습니다. 물류센터를 상상해보세요. 서울 지역의 a 창고와 경기 지역의 a 창고는 물리적으로 다릅니다. Script1과 Script2도 물리적으로 다르기 때문에 창고 이름이 동일하더라도 문제가 되지 않습니다. 하지만, 모든 물류 창고가 모이는 중앙 물류센터는 a라는 창고가 2개 존재할 수 없습니다. 이렇게되면 물류센터 작업장에서 일하는 직원들이 어디에 물건을 넣어야할지 모르기 때문입니다. 매크로 프로그램도 이와 동일한 방식이라고 이해하면 됩니다.

     

    이제 토큰 목록에 추가된 플레이스홀더들을 변수와 치환해야 합니다. 아래와 같이 변수 레벨에 따라서 처리할 수 있습니다.

    foreach (var token in tokens)
    {
        switch (token.Item1)
        {
            case "G":
                if (player.Manager.Variables.ContainsKey(token.Item2))
                    origin = origin.Replace("{{{" + token.Item2 + "}}}", player.Manager.Variables[token.Item2].ToString());
                break;
            case "P":
                if (player.Parent != null && player.Parent.Variables.ContainsKey(token.Item2))
                    origin = origin.Replace("{{" + token.Item2 + "}}", player.Parent.Variables[token.Item2].ToString());
                break;
            case "L":
                if (player.Variables.ContainsKey(token.Item2))
                    origin = origin.Replace("{" + token.Item2 + "}", player.Variables[token.Item2].ToString());
                break;
        }
    }

     

    플레이어를 메소드의 인자로 넘기는 이유는 아래와 같이 사용자에게 정보를 제공하기 위함입니다. Output에 WriteLine 메소드를 사용하려면 아래와 같이 작성해야 합니다.

    player.Manager.Output.WriteLine($"{player.Manager.Client.ResxCaption.GetString(property.Name)}:{origin}");

     

    아래는 전체 코드입니다.

    public static string? GetMatches(IPlayer player, PropertyInfo property, string? pattern)
    {
        if (string.IsNullOrEmpty(pattern))
            return pattern;
    
        var tokens = new List<Tuple<string, string>>();
    
        string origin = pattern;
        pattern = Regex.Escape(pattern);
    
        pattern = Regex.Replace(pattern, @"\\{\\{\\{.*?}}}", (match) =>
        {
            var name = match.Value.Substring(6, match.Value.Length - 9);
            tokens.Add(new Tuple<string, string>("G", name));
            return $"(?<{name}>.*)";
        });
    
        pattern = Regex.Replace(pattern, @"\\{\\{.*?}}", (match) =>
        {
            var name = match.Value.Substring(4, match.Value.Length - 6);
            tokens.Add(new Tuple<string, string>("P", name));
            return $"(?<{name}>.*)";
        });
    
        pattern = Regex.Replace(pattern, @"\\{.*?}", (match) =>
        {
            var name = match.Value.Substring(2, match.Value.Length - 3);
            tokens.Add(new Tuple<string, string>("L", name));
            return $"(?<{name}>.*)";
        });
    
        if (tokens.Count == 0)
            return origin;
    
        foreach (var token in tokens)
        {
            switch (token.Item1)
            {
                case "G":
                    if (player.Manager.Variables.ContainsKey(token.Item2))
                        origin = origin.Replace("{{{" + token.Item2 + "}}}", player.Manager.Variables[token.Item2].ToString());
                    break;
                case "P":
                    if (player.Parent != null && player.Parent.Variables.ContainsKey(token.Item2))
                        origin = origin.Replace("{{" + token.Item2 + "}}", player.Parent.Variables[token.Item2].ToString());
                    break;
                case "L":
                    if (player.Variables.ContainsKey(token.Item2))
                        origin = origin.Replace("{" + token.Item2 + "}", player.Variables[token.Item2].ToString());
                    break;
            }
        }
    
        player.Manager.Output.WriteLine($"{player.Manager.Client.ResxCaption.GetString(property.Name)}:{origin}");
        return origin;
    }

     

    매크로 에디터를 실행하고, 아래와 같이 스크립트를 구성하세요.

    변수 a: John

    변수 b: NGMsoftware

    gbt2Aaz.png

     

     

    텍스트 체크 액션의 왼쪽 값에 아래와같이 플레이스홀더를 적용했습니다.

    • Hello {a}, Welcome to {b}!

    NuJpeoB.png

     

     

    매크로를 실행하고, 결과를 확인해보면 아래 동영상처럼 하단의 아웃풋에 플레이스홀더 결과를 확인할 수 있고, 실제 액션의 속성은 변화하지 않습니다.

     

     

    스크립트는 저장했다가 불러와서 다시 원래처럼 동작해야 합니다. 따라서, 플레이스홀더로 변환된 값이 액션의 속성에 기록되면 문제가 될 수 있습니다. 물론, 스크립트를 저장하지 않으면 문제가 없겠지만, 실행 후 저장하면 Hello {a}, Welcome to {b}! 값이 Hello John, Welcome to NGMsoftware!로 변경됩니다. 이렇게되면 변수의 값이 변화하더라도 플레이스홀더는 더이상 반영되지 않는 문제가 발생합니다. 그래서, 원본 속성값은 유지하되 변화된 값을 사용자가 쉽게 확인할 수 있도록 아웃풋(출력창)에 별도로 표시합니다.

     

    개발자에게 후원하기

    MGtdv7r.png

     

    추천, 구독, 홍보 꼭~ 부탁드립니다.

    여러분의 후원이 빠른 귀농을 가능하게 해줍니다~ 답답한 도시를 벗어나 귀농하고 싶은 개발자~

    감사합니다~

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.