얼마전 유니티에 대한 코딩 컨벤션(코딩 큐칙)에 대해 찾아보다가 재미있는 약자를 발견했습니다. 바로 DRY와 WET입니다. '젖다'와 '마르다'를 뜻하는 두 반의어인데요. 이것이 프로그래밍 약자에서도 동일하게 반의어로 쓰이고 있더라구요.
먼저 DRY는 'Don't Repeat Yourself'
의 약자입니다. 즉, '자기자신을 반복하지 말라'라는 의미로 코드의 반복을 피하라는 의미입니다.
그리고 WET은 'Write Everything Twice'
라는 뜻으로, 모든 코드를 두번씩 쓰게 되는 코드 반복성에 대해 풍자적으로 말하는 표현입니다. 'Waste Everyone's Time' 으로도 쓰인다고 합니다. 반복되어 정리되지 않은 코드는 접하는 모든 사람의 시간을 낭비하게 되죠.
Be DRY나 Don't be WET 둘 다 코드의 반복을 피하라 라는 동일한 뜻을 갖게 되는 것입니다.
말그대로 반복을 줄인 코드입니다. 동일한 코드는 모두 함수화를 시키거나, 프로퍼티로 변경하도록 합니다. 반복되는 코드의 볼륨이 크다면 모두 함수로 바꾸고 바꾼 함수들을 모아 클래스화 시킬 수도 있겠네요.
현재 진행중인 실내자전거 트레이닝 프로그램으로 간단한 예시를 들어보도록 할게요.
가상 자전거 트레이닝을 위해서는 현재 가상 캐릭터가 위치해있는 곳의 경사도에 알맞는 저항을 계산해서 실내자전거에 부하를 걸어주어야 합니다. 따라서 경사도는 실내자전거에 데이터를 보낼 때도 사용되고 UI에 현재 경사도를 표시할 때도 사용됩니다.
반복을 피하려 노력하지 않았다면 이런 코드가 쓰입니다.
// BikeController.cs
private void OnGUI()
{
GUILayout.Label("경사각: " +
Mathf.Repeat(transform.rotation.eulerAngles.x + 180f, 360f) - 180f + "도");
GUILayout.Label("경사도: " +
Mathf.Tan(Mathf.Repeat(transform.rotation.eulerAngles.x + 180f, 360f) - 180f) *
Mathf.Deg2Rad * 100f + "%");
}
// SimulatorConnector.cs
private void FixedUpdate()
{
SendData(GetSlopeByte(
Mathf.Tan(Mathf.Repeat(bikeController.transform.rotation.eulerAngles.x + 180f, 360f) - 180f) *
Mathf.Deg2Rad * 100f));
}
벌써부터 엄청나게 복잡하게 보입니다.
만약 여기서 반복되는 코드들을 함수로 바꾼다면 어떻게 될까요.
// BikeController.cs
public float InclineAngle()
{
float angle = transform.rotation.eulerAngles.x;
angle = Mathf.Repeat(angle + 180f, 360f) - 180f;
return angle;
}
public float InclineSlope()
{
return Mathf.Tan(InclineAngle * Mathf.Deg2Rad) * 100;
}
private void OnGUI()
{
GUILayout.Label("경사각: " + InclineAngle() + "도");
GUILayout.Label("경사도: " + InclineSlope() + "%");
}
// SimulatorConnector.cs
private void FixedUpdate()
{
SendData(GetSlopeByte(bikeController.InclineSlope()));
}
코드의 라인은 조금 늘어났지만 훨씬 보기 편한 모습입니다.
제가 생각하는 DRY의 장점은 대충 아래와 같습니다.
코드를 읽기가 쉽다.
DRY한 코드는 자연스럽게 코드의 길이가 짧아지게 됩니다. 또한, 한번 함수의 코드를 읽게 되면 이후로는 동일한 코드를 다시 읽을 필요없이 해당 함수의 의미를 곧바로 파악이 가능합니다. 그러므로 함수가 반복되어 쓰이더라도 읽고 생각하는 시간을 소모하지 않아도 됩니다.
예시에서 Mathf.Repeat
나 Mathf.Tan
같은 왜 사용됐는지 생각해야하는 함수들이 첫번째 코드에는 여러번 쓰여서 계속해서 생각해야했습니다. 그러나 두번째 코드에서는 InclineSlope()
라는 함수를 한번만 읽어본다면 이후로는 '경사도구나' 라는 생각만 하면 되기 때문에 코드를 읽기가 훨씬 수월합니다.
뛰어난 재사용성을 갖추게 된다.
처음에는 한두번만 쓰일 것 같은 코드도 코딩을 하다보면 의외로 여러곳에 쓰일 때가 많이 있습니다. 이럴때 미리 함수화 또는 프로퍼티화를 시켜놓았다면 빠르게 재사용할 수 있습니다.
만약 경사도에 따라서 특정이벤트를 발생시키려는 기능이 필요하다면 간편하게 추가할 수 있습니다.
//EventManager.cs
if(bikeController.InclineSlope() > 15)
{
steepCrash.Invoke();
}
비용이 줄어든다.
앞서 말한 것과 같이 코드의 길이가 줄고 읽기가 쉬어 코드를 읽는데 걸리는 시간을 줄일 수 있습니다. 또한 코드의 오류를 찾고 수정할 때 걸리는 시간도 줄어들게 됩니다.
코드를 유지보수하기가 쉽다.
DRY한 코드는 필연적으로 작은 조각으로 나누어지게 됩니다. 결론적으로 코드의 복잡도와 의존성이 줄어들어 코드를 변경하거나, 기능을 추가하는 등의 작업할 때 훨씬 수월합니다.
사실 DRY 말고도 수많은 개발원칙들이 있습니다. 객체지향원칙인 SOLID도 있고, 앞서 소개드린 DRY와 더불어 3대 원칙으로 불리는 KISS(Keep It Simple, Stupid)나 YAGNI(You Ain't Gonna Need It) 들도 있습니다.
사실 이 원칙이라는 친구는 보기에는 매우 간단하고 합리적이라고 생각하지만, 막상 실천하기는 참 어려운 것입니다. 개발 원칙도 마찬가지입니다. 계속해서 상기시키고 노력하지 않으면 실천하기가 참 어렵습니다. 하지만 꼭 필요하고 중요한 것이기에 항상 유념해야겠습니다.