티스토리 뷰

이번에 팀 협업을 하면서 느낀 점, 그 중 파일 구성 설계에 대해서 서술하려고 한다.

※ 마지막에 총 정리가 있습니다.

파일 구성 설계를 하는 이유

Git을 쓰던, svn을 쓰던 협업을 한다고 하면 결국 같은 프로젝트 내에 개발자가 복수 배치되는 게 일반적이다.

비단 개발자가 아니더라도 기획자나 아트, 기타 구성원들도 프로젝트를 계속해서 수정하는 게 현대의 개발 환경이며, 이 개발 환경에 의해서 발생하는 문제가 현대의 개발 환경에 가장 큰 숙달 요소라고 보는 것도 가능하다.

그렇다면 이렇게 '같은 요소를 동시에 작업하는 것'의 가장 큰 난점은 무엇일까.

 

프로그래밍을 배우다 보면 락(Lock) 내지는 세마포어(Semaphore)라는 개념을 익히게 된다.

이러한 '잠그는' 기능을 사용하는 이유는 '같은 파일을 동시에 변경할 경우, 그 이후의 대처'에 대해서 문제가 발생하기 때문이다. 물론, 이러한 잠금 시스템으로 인해서 순환형으로 락이 걸려서 시스템이 멈춰버리는 데드락(Deadlock) 문제도 있지만, 기본적으로는 락 자체는 이러한 프로세스 간의 협업에서 생기는 문제를 막기 위한 기능 중 하나인 것이다.

 

그렇다면 협력 개발 환경에서 그러한 역할은 무엇일까.

사실, 의외로 간단하게 답을 도출할 수 있다. 바로 '규약'이다.

락이나 세마포어도 결국에는 다른 프로그램을 사용하는 동안 해당 요소를 잠궈두는 고정 루틴, 즉 규약을 활용하여 제작한다. 그렇다면 협력 개발 환경에서도 이러한 규약 내지는 약속이 해당 역할을 수행한다고 보는 것이 옳을 것이다.

 

자, 그렇다면 '다른 사람이 해당 파일을 수정하고 있을 때에는 해당 파일을 건들지 않는다.'라는 규약을 걸어놓았다고 상정 해 보자. 그리고 '다방면의 기능을 모두 담고 있는 하나의 파일이 있다'고 가정하자.

그렇다면 앞서 개발하는 한 명이 모든 작업을 끝낸 뒤에야 다음 작업자가 해당 파일을 작업할 수 있을 것이다. 설사 서로 다른 기능을 제작한다고 하더라도.

 

파일 구성 설계를 하는 이유가 바로 이것이다. 물론, 가독성을 높이기 위한 목적도 있기는 하나, 협업 환경에서 서로가 서로에 의해서 업무가 막히는 경우를 최소화하는 것이 가장 큰 목적이라고 봐도 되는 것이다.

그렇기 때문에 협업 능력 측면을 생각하면 파일 구성은 최대한 작은 단위로 쪼개야 하는 셈인데, 문제는 하나의 기능을 담당하는 요소를 두 곳에 나눠서 넣으면 그건 스파게티 코드라는 점. 때문에 이러한 파일 구성을 설계할 때에는 과하지도, 덜하지도 않은 적정선을 지키는 것이 가장 중요하다.

 

그렇다면 이제 파일 구성 설계를 하는 기준에 대해서 알아보자.

 

파일 구성 설계를 하는 방법.

기본적으로 파일 구성 설계를 할 때, 해당 파일에 무엇을 작성할 지에 따라서 설계를 하게 된다. 다르게 말하면 무엇을 작성해야 하는지를 모른다면 파일 구성을 해도 의미가 없게 된다.

그렇기에 파일 구성 설계를 하기 전에 각 요소들의 전체적인 스펙이나 용도 등을 정리하고 시작해야 한다는 것을 명심하도록 하자.

 

각 기능의 용도 및 분류

간단하게 말해서, 해당 클래스가 담당하는 역할을 파악해야 한다는 것이다.

예를 들면, 플레이어 캐릭터는 캐릭터에 속해있는 플레이어의 조작을 반영하는 요소인 셈으로, 시스템이 아니라 캐릭터로 분류된다. 특히 유니티에서는 이 플레이어 캐릭터는 씬이 넘어가면 파괴되는 오브젝트로 제작하게 되는데, 다르게 말하자면 항시 유지되야 하는 퀘스트나 인벤토리 등은 플레이어에 들어가면 안된다는 의미가 된다.

 

하물며, 엄밀히 말해서 인벤토리와 퀘스트는 플레이어에 속해있는 게 아니라 시스템에 속해있는 요소들이다.

이러한 소속 관계와 그 용도를 확실히 파악하고, 개별로 구성되는 요소는 다른 파일으로 나눠서 작성해야 한다는 것이다.

 

공통으로 사용해야 하는 요소들

간단하게 말하면 Manager 계열 파일로 작업해야 하는 것들이다. 이 Manager들은 여러 기능에서 공통적으로 사용하는 요소들이 들어가게 되는 파일이기에, 필연적으로 여러 사람이 동시에 다루게 된다.

그런 상황에서 특정 Manager 파일이 여러 기능을 담고 있다고 가정해 보자. 결국 해당 Manager에 속해있는 기능을 담당하는 사람들은 상술했던 순차적 작업 문제가 발생하게 될 거고, 그렇다면 결국 개발 상황이 지지부진할 수 밖에 없는 것이다.

 

때문에 Manager파일들은 잠깐 만 작업하고 바로 커밋할 수 있는 환경을 만들거나, 한 번 완성하면 더 이상 손을 대지 않도록 구성해야한다고 정리할 수 있으며, 이에 따라서 Manager 파일들은 보유한 변수와 함수가 최대한 적어야 한다고 정리할 수가 있는 것이다.

 

항시 유지되야 하는 요소들

상술했던 퀘스트나 인벤토리와 같이, 어떠한 상황에도 유지되어야 하는 요소들을 의미한다.

이러한 요소들은 기본적으로 개별적인 시스템으로 구성되는 경향이 있기 때문에 별개 파일으로 작성해야 하는 것도 있지만, 캐릭터와 같이 특정 상황에만 유지되는 요소와 분리하기 위해서 별개 파일로 작성해야 한다.

[물론, 같이 작성해도 되긴 하나, 불필요한 데이터도 항시 유지해야 하기 때문에 메모리 상으로 비효율이 발생한다.]

 

사실, 코딩 스타일에 따라서 Manager 계열 파일과 유사 혹은 동일의미로 쓰기도 하는 개념이다.

그래도 일단 분리해서 적었으니, 개인적으로 사용하는 분류 기준을 정리하자면 아래와 같다.

  1. Manager계열 파일들은 싱글톤으로 구성되어 파일들 간의 중계역할을 하거나 프로젝트의 중추 역할을 한다.
  2. 단순히 항시 유지될 뿐인 요소들은 그저 시스템 중 하나를 담당하는 경향이 있다.

이 점 때문에 본인은 개인적으로 이러한 파일들은 이를 유지하기 위한 Manager를 하나 정해서 해당 객체가 들고 있도록 만드는 편이며, 그냥 아예 이러한 파일들도 싱글톤으로 구성하는 경우도 있는 것으로 알고 있다.

 

각 객체들 간의 의존성

의존성을 구비해야 한다는 내용이 아니라, 의존성을 배제해야 한다는 의미다.

각 클래스 간의 의존성이 높으면, 그 의존성이 높은 클래스의 완성도에 해당 클래스가 휘둘리게 된다. 이로 인해서 개발 프로세스도 의존성 높은 객체에 의해서 막혀버리게 되고, 그 결과 분명 다른 파일임에도 단일 파일을 순차적으로 개발하는 것이나 큰 차이가 없게 되는 문제가 발생한다.

더욱이 이 요소는 스파게티 코드와도 관계가 있는 요소이기 때문에 반드시 고려를 해야하는 요소인 것이다.

 

이를 해결하기 위해서 각 객체가 사용하는 요소들은 1차적으로 해당 객체가 직접 들고 있어야 하고, 그게 불가능한 경우에나 2순위 해결책으로 외부 요소를 끌어와서 간접적으로 사용하는 것을 선택해야 한다.

필드(변수)가 늘어난다던지, 함수의 수가 늘어난다던지 하는 것은 크게 신경쓸 필요가 없다. 오히려 과도하게 줄이려고 할 때에 가장 잘 발생하는 것이 스파게티 코드라는 점을 마음에 담고 있어야 할 것이다.

[그렇다고 너무 많이 만들어도 문제니까, 이쪽도 최소단위로 자르자. '메소드'라는 개념이 괜히 있는 것이 아니다.]

 

정리해서, 파일 구성 설계 방법

각 기능의 적용 범위를 설계, 분류하여 개별 편성하고, 이들 중 공통으로 사용하거나 항시 유지되어야 하는 요소들 '만' 추려서 용도 별로 분류하여 GameManager 등의 싱글톤 클래스에 통합 작성하면서 각 객체들 간의 의존성은 낮춰야 한다.

이렇게 정리해서 보면 상당히 간단하게 보이지만, 의외로 이렇게 구성하기가 쉽지는 않다. 이는 직접 협업을 해 보면 느끼게 될 것이다. 다르게 말하면 경험을 많이 쌓아야지 볼 수 있는 영역이기도 할 것이라는 의미이기도 하다.

 

일단 현재로서 내가 생각하고 있는 설계 방법은

  1. 자신이 '시스템'이라고 생각되는 요소와 '객체'라고 생각되는 요소는 모두 별개의 파일으로 구성하고,
  2. 시스템이나 객체의 종류가 동일한 종류로 분류할 수 있다면 이들은 인터페이스로 묶으면서,
  3. 서로 다른 객체나 시스템에서 공통적으로 사용해야 하는 요소들은 GameManager나 적당한 Manager 파일으로 정리하는 것이다.
  4. 그러면서 유동적으로 작동해야할 것들은 제너릭과 델리케이트로 정리하는 것이 어떨까 싶다.
    [제너릭과 델리게이트는 서로 용도가 다르니, 이에 유념하여 구성하도록 하자.]
    • 제너릭은 '변수형'에 대한 유동성이다. 이 때문에 미리 설계된 유동성이라고 봐야한다.
    • 델리게이트는 '함수 사용'에 대한 유동성이다. 이 때문에 상황에 따른 유동적인 대처법으로 봐야한다.

 

그리고, 아무리 파일 이름이라고 해도 너무 많이 늘어져 있다면 알아보기 힘들다. 

서로 같은 분류에 해당한다면 같은 폴더로 묶어주자.

 

번외. 예외사항

만약에 파일 입출력과 관계된 부분이 있다면, 해당 부분은 반드시 관련 기능과 따로 떨어뜨려서 작성해야 한다.

또한, 해당 부분은 관련 기능이 모두 완성된 이후에 작업해야 한다.

물론 파일 입출력이 상당히 골 때리는 작업인 것은 사실이다. 조금만 어긋나도 NullException을 도출한다거나, 코딩 툴에서 빨간줄이 없는데도 에러가 속출한다거나, 에러가 발생하는 부분이 파일 입출력과는 관계가 없다거나 하는 경우가 잦아서 이에 비중을 크게 두는 것도 심적으로는 이해가 된다.

하지만, 이를 지키지 않고 파일 입출력을 먼저 만들게 된다면 파일 입출력이라는 틀에 갇혀서 옴짝달싹 못하는 코드를 보면서 절망하게 될 것이다. 반드시 지키도록 하자.

 

정리하며

사실 이번에 작성한 내용은 이전에도 별 생각 없이 지키고 있었던 부분이기도 합니다.

다만, 이번에 협업을 하면서 사전에 이런 작업을 하려고 생각하니까 바로 답이 안나와서 당황했었어요.

이번에는 다른 팀원이 이 역할을 수행했지만, 이후 제가 이 역할을 수행하게된다면 버벅이지 않고 잘 할 수 있도록 정리해 보았습니다.

정리하고 나니까 새삼 당연한 내용이었다 싶긴 한데, 그래도 정리하기 전 까지는 어떻게 표현을 못했던 사항인지라, 정리하기를 잘 했다고 생각합니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함