부트캠프/본캠프

[내일배움캠프_2025JUL15] 스크럼 마스터, 스킬 구현, 그 놈의 JSON

Young_A 2025. 7. 15. 19:55

목차

    스크럼 마스터, 스킬 구현, 그 놈의 JSON

     

     

    수영 다녀온 날은 수영모 때문인가 두통이 너무 심하다 😐

     


    스크럼 마스터

    팀 내 개발 리드로서 스크럼 마스터 역할을 맡게 되었다.

    일 2회 진행하게 되었는데, 진행하면서 얻은 장점이 많았다.

     

    1. 팀원들의 목표 구체화 및 진행도 파악
    2. 겹치는 작업 구역 확인 및 업무 재분배
      • BS님과 NH님의 영역이 인벤토리와 상점으로, 아이템 구매 및 판매 영역이 겹쳤었다. BS님이 먼저 진행하신 것을 스크럼에서 발견하여 NH님이 중복으로 작업하지 않을 수 있었다.
    3. 팀원들의 애로사항 확인 및 해결법 정리
      • 내가 구조를 짰기 때문에, 제작한 Scene들을 어떻게 Program.cs에서 실행할 수 있는지 모르는 분이 많아서 스크럼 기록에 정리해서 남길 수 있었다.
      • 이 부분은, 일자별 스크럼 기록보다는 별도 문서로 정리하여 팀 개발 중 팀원들이 상시 확인할 수 있는 문서로 발전시키면 더 좋을 것 같다. 

     

    이 정도가 될 것 같다.

     

    단점은 일 2회 스크럼으로 인한 시간 소모.

    10분에서 15분 정도로 끝나긴 하지만, 질문들을 처리하면 (내 입장에서) 총 30분에서 한시간까지도 걸리는 것 같다.

    근데 생각해보면 스크럼으로 인해 일찍 파악해서 모두의 시간을 아낀 것이 아닐까?


    Console 창 크기

    콘솔 창 크기를 세팅 할 수 있다.

    Console.SetWindowSize(200, 50);	//현재 콘솔 창의 크기를 조절해줌.
    Console.LargestWindowHeight;	//최대 콘솔 높이 반환
    Console.LargestWindowWidth;		//최대 콘솔 너비 반환
    
    int maxWidth = Console.LargestWindowWidth;
    int maxHeight = Console.LargestWindowHeight;
    Console.WriteLine($"{maxWidth}{maxHeight}");
    
    Console.SetBufferSize(80, 100); //빈 줄을 포함한 전체 줄 수가 늘어나서 스크롤이 가능해짐.

    JSON 파일 저장 방법

    게임을 어떻게 저장하면 좋을까?

    떠오른 방법은 두가지.

    1. 하나의 Json 파일에 플레이어 이름을 Key 값으로 받는 Dictionary를 생성하여 직렬화한다.
    2. 각 플레이어별 save_플레이어이름.json 파일을 생성한다. (Week2 TextRPG와 동일)

    1번의 경우,

    • 장점: 파일이 하나라서 관리가 단순해보인다.
      • 플레이어 끼리의 기록을 경쟁한다거나 시스템을 도입할 경우 비교하기 쉬울 것 같다.
    • 단점: 플레이어 수 증가로 로딩/저장이 비효율적이다.
      • 하나만 수정해도 전체 데이터를 새로 저장해야하고, 만약 손상이라도 생기면 모든 것이 날아간다

    2번의 경우,

    • 장점: 저장/로드가 간편해지고 확장성이 좋아보인다.
      • 다른 플레이어 데이터에 영향을 주지 않으므로, 오히려 관리가 간편해질 수도 있겠다.
    • 단점: 파일이 여러개라서 관리가 힘들 수 있다.
      • 여러 파일 중 중복된 파일이 있다면 그대로 덮어쓰니 데이터 손실이 우려된다.

    장단점을 취합하여 채택한 방법은 지난주 개인과제를 할때와 같이 2번 방법이다.

    중복된 파일이 있을 수 없게 유효성 검사를 진행하는 것이 더 편할 것 같다.

    또한, 팀원들이 테스트 플레이를 하면서 생성되는 더미 플레이어들 삭제에도 편할 것 같다.

    실제 배포를 하지 않고, 개발만 진행하는 게임이니 2번 방법이 적합해보인다.

    여러종류의 데이터 직렬화하기

    이전 게임과 달리, Player만 저장해서는 부족하다.

    도감 시스템, 던전 해금 시스템이 도입될 예정이므로, 이와 관련된 내용을 전부 저장해야한다.

     

    List<object>로 진행할까? 했지만 JSON 파일 가독성이 엄~청 안좋을 것 같다는 생각이 들었다.

    통일되지 않은 데이터이므로 저장할 항목이나 종류가 추가될수록 꼬일 것 같았다.

    SaveData 클래스를 아예 따로 만들어서, 진행하기로 했다.

    internal class SaveData //현재까지 플레이중인 정보를 저장
    {
    
        public Player PlayerToSave;
        //그 외에 도감, 던전 진행도 등 저장할 것들 필드로 삼고, 생성자에 입력. 
        public SaveData() 
        {
            PlayerToSave = PlayerManager.Instance.CurrentPlayer;
        }
    }

     

    아직은 Player 밖에 없지만, 추후 다른 기능들이 해금되면 여기에 추가하고, 생성자에 초기화 해주면 되는 것이다!

    JSON 순환참조 직렬화

    JSON으로 파생된 문제는 끝나지 않는다...

    Player는 Inventory를 갖고, Inventory 또한 Player를 참조한다.

    Database로 치면, Player 1-many Items many-1 Inventory와 같이 중간 테이블을 생성해주면 좋을텐데,

     지금 당장 거기까지 가는 건 오버일 것 같아서 순환참조를 했었다.

     

    JSON은 바로 순환참조를 했다며 징징거리기 시작했고...

    [JsonIgnore]를 사용해 이 녀석은 직렬화하지 말라고 끊어주었다.

    이럴 경우, 역직렬화시에 다음과 같이 다시 연결해주어야한다.

    [JsonIgnore]
    public Player Owner { get; private set; }
    
    //SaveManager.cs LoadGame()
    SaveData.PlayerToSave.Inventory.SetOwnerAgain(SaveData.PlayerToSave);   //직렬화시 순환 끊었던 것 다시 설정.

    * 실행하기 전에 Newtonsoft.Json의 JsonIgnore 속성을 사용하고 있는지 확인해보자

    추상 클래스 역직렬화

    Skill 같은 경우는 추상 클래스로, 이를 상속 받는 AttackSkill과 HealSkill이 있다.

    하지만 SkillList은 Skill 타입의 List이므로...

     

    Json을 역직렬화하면서, 추상 클래스인 Skill 타입을 직접 인스턴스화 할 수 없다!

     

    따라서 JsonSerializerSettings 에서 TypeNameHandling.All 옵션을 켜면

    직렬화 시 타입 이름이 함께 저장되고, 역직렬화 시에는 정확한 구체 클래스 타입으로 생성할 수 있게 된다.

    JsonSerializerSettings setting = new JsonSerializerSettings()
    {
        TypeNameHandling = TypeNameHandling.All
    };

    Deep Copy

    List, Dictionary, object 같은 경우, 아래와 같이 대입하면 "값 복사"가 아니라 "참조 복사"를 진행한다.

    CurrentPlayer = SaveManager.Instance.SaveData.PlayerToSave;

    따라서 CurrentPlayer를 변경하면 PlayerToSave도 변경되는 것이다.

     

    지금처럼 싱글플레이 게임이라면 당장에 문제는 없겠지만 되도록 피하는 것이 좋다.

    둘 다 변경됨으로서 명확한 책임 구분이 깨지게 되고, 유지보수 및 확장 시 위험해진다.

    public void Clone(Player player)
    {
        Id = player.Id;
        Name = player.Name;
        HP = player.HP;
        MP = player.MP;
        Attack = player.Attack;
        Defense = player.Defense;
        SkillList = new List<Skill>();
        foreach (Skill skill in player.SkillList)
        {
            SkillList.Add(GameDataManager.Instance.AllSkills.FirstOrDefault(x => x.Id == skill.Id));
        }
        IsAlive = player.IsAlive;
        JobType = player.JobType;
        Inventory.Clone(player.Inventory);
        NumOfStones = player.NumOfStones;
    }

     

    따라서 위와 같이 값 복사를 해야하는 클래스에 Clone() 메서드를 붙여주었다.

    만약 Player 내부에서도 참조 복사를 하는 객체가 있다면, 마찬가지로 해당 클래스에 Clone() 메서드를 붙여주어야한다.

    추상 클래스의 경우, Clone() 추상 메서드를 만들고, 자식 클래스들에게 상속해주면 된다.

     

    Clone() 메서드 대신, 같은 타입을 받는 생성자로도 할 수 있는 것 같다.

    public Player(Player loadedPlayer)
    {
    	//Deep Copy 진행
    }

    느낀점

    스크럼을 통해 팀원들의 애로사항을 파악하고, 그를 쉽게 이해할 수 있도록 알려주는 역할을 많이 했다.

    잘 따라와주시는 팀원들 덕분에 리드하는 것에 대한 뿌듯함을 느낄 수 있었다.

     

    개발에 걸리는 시간이 예전보다 줄어든 느낌이다.

    구조 설계를 하고, 구조에 대한 이해도가 늘어가면서 개발에 속도를 낼 수 있는 것 같다.


    내일 학습 할 것은 무엇인지

    던전 할당을 안해서 마을에서 던전 진입 - 전투 로의 흐름이 끊겨있었다.

    빠뜨린 던전을 진행해야한다.

     

    아이템을 구매할 때, 상점과 인벤토리로 분리되어 있는 Store.SellToPlayer와 Inventory.PurchaseItem이 분리된 구조가 이해가 잘 안된다는 얘기가 있어서 내일 오전에 구현하고 오후 스크럼 때 설명해드리기로 했다.

     

    앞으로 7시 이후에는 백업 브랜치를 하나씩 팔 예정이다.

    그리고 백업 브랜치 기준으로 다음날 오전 스크럼 때 피드백을 드리려고 한다.

    피드백은 구조 설계 상 팀 개발 규칙에 알맞지 않은 것, 혹은 사용하지 않는 유틸리티가 있다면 안내해드리는 것 등을 할 예정이다.

    이런 사소한 것들은 사실 개인 개발을 할 때는 문제가 되지 않지만, 팀으로 개발 할 때는 효율에 문제가 생길 것 같다.

    쌓이지 않게 매일매일 관리할 수 있도록 진행할 생각.

     

    +업로드 하고 나니까 이미지가 없으니 아쉬워서... SI님이 만드신 타이틀! 이름도 공모하신거다!

     

    시간이 좀 남아서 프로그래머스 쉬운 문제들을 풀었다.

    그 중에 틀렸던 부분들만 정리해서 알고리즘 카테고리에 포스팅 했다.

     

    [프로그래머스] 자연수 뒤집어 배열로 만들기 (C#)

    프로그래머스 - 자연수 뒤집어 배열로 만들기Problem Description자연수 n을 뒤집어 각 자리 숫자를 원소로 가지는 배열 형태로 리턴해주세요. 예를들어 n이 12345이면 [5,4,3,2,1]을 리턴합니다. Constraints:

    j000.tistory.com