C와 객체지향 – 01 – 시작

C언어에 대한 글을 쓰고 있다. 리눅스 환경에서 동작하는 C언어를 쓰면서 C언어의 기본적인 내용을 작성하였고, 리눅스 환경에서 C언어를 개발하는데 이용하는 라이브러리들, 그리고 gcc와 make에 관한 글을 작성하고 있습니다.

그런데 문득 작업 진행이 제대로 이루어지고 있지 않는다는 것을 깨달았다. 이젠 리눅스 환경에서의 사용이 중점이기 때문에 그쪽 자룔 정리해서 올리는 작업이다 보니 언어에 관한 작업이 더뎌지고 해서 그런지 작업 속도가 늘지 않았다. 졸업논문 쓰고 난 후의 피로감도 있고…

그래서 이와는 별개로, C에 대한 내용을 작성하였으니 이젠 C 자체만에 추가적인 내용을 적어보고자 합니다. 그게 바로 고급지게 쓰는 C입니다.

그 중에 첫번째로 바로 C와 객체지향에 대해서 작성해 보려고 합니다.

C는 절차지향적 언어라고 기본적으로 배우죠. 따라서 프로그램의 흐름이 중요하고, 그에 맞도록 함수를 잘 써서 분할하고 하는 작업에 대해서 중요하게 여깁니다. 그러다 보니 아무리 몇만 라인의 코드를 작성하더라도 코드 자체가 상당히 평평한 구조를 가지게 됩니다. 함수 하나만 해도 몇백 라인이 되기 일쑤고, 비슷한 기능 하는 함수들 미친듯이 만들고, 그런 함수들이 뒤에 숫자 2,3,4 이런 식으로 막 붙은 그런 함수들… 양산하기 무지 쉽습니다. (이 글을 읽어야 할 수준이라면 내 이야기 아니라고 생각하는 분 없을겁니다.)

내부 처리 로직 중에서는 공통으로 처리할 수 있는 부분은 공통으로 처리할 수 있지만, 동작 제어 함수들은 좀 사정이 다릅니다. 다양한 인자가 필요한 함수, 사용하는 데이터가 전역 변수로 연결, 독립성 결여된 함수들, 서로 의존관계가 너무 깊어 개별적인 테스트 할 수 없는 함수들….

사실 C에서는 특정 내용을 처리하기 위해 함수라는 것이 존재하고 이를 이용해 프로그램을 구조화할 수 있어야 한다고 가르치는데 왜 이런 현상이 발생할까요?

바로 디자인 패턴의 부재 때문입니다. 소프트웨어 개발에서 말하는 디자인 패턴은, 프로그램 개발에서 자주 나타나는 과제를 해결하기 위한 방법 중 하나로, 과거의 소프트웨어 개발 과정에서 발견된 설계의 노하우를 축적하여 이름을 붙이고, 이를 재사용하기 좋은 형태로 특정 규약을 묶어서 정리한 내용을 말합니다. 문제 해결을 위해 해결 방법을 제시하고, 이를 사용 가능한 구조로 묶고 코드로 만들 수 있는 단계를 알고리즘이라고도 하기 때문에 알고리즘을 가르치는 것인가를 생각하기 쉽지만 디자인 패턴에서는 구조적으로 이미 정의된 문제를 해결하는 방식을 알려줍니다. 이미 정형화된 패턴을 문제에 적용하는 점이 아무것도 없는 상태에서 하나 하나 설계하는 중에 최적화 작업을 하는 알고리즘과는 접근 방법이 조금 다르죠.

그리고 이런 디자인 패턴의 많은 부분이 객체 지향 언어에서 사용하는 것을 전제로 하기 때문에 C랑은 상당히 무관하다는 듯이 배우고, 가르치는 분들 또한 그렇게 가르칩니다. 컴퓨터공학 전공한 분들을 아시겠지만, 객체 지향 프로그래밍 과목을 수강한 후에 디자인 패턴에 대한 강의를 들을 수 있다는 것을 알 수 있습니다.

그러나, C에서도 객체지향을 실현할 수 있습니다. 객체 지향을 잘 활용하면 프로그램의 구조가 기존의 C 프로그램들과는 달리 크게 변하게 됩니다. 또한 각 함수들이 독립적으로 작동하게끔 만들 수 있습니다. 이를 위해서는 C로 객체 지향을 염두해 두고 설계하고 프로그래밍할 수 있어야 합니다. 특히, 현대의 임베디드 시스템들과 C 프로그래밍에서는 이런 사고가 상당히 많이 적용되어 있기 때문에 C를 할 수 있다고 하려면 이런 수준까지 할 수 있어야 합니다.

그래서, C로 객체 지향과 같은 설계와 프로그래밍을 할 수 있는 방법을 포스팅하려 합니다.

p.s. 기존의 리눅스 환경에서의 C 프로그래밍과 별도로 활동합니다.

어정쩡한 확장하면 결국 원래 하던 사업으로 돌아가게 된다

과거 경험 이야기다. 이력서 쓰다보니 든 생각이다. 잡소리로 들어줬으면 하는 글이다.

2010~2011년 당시, 사람들이 미친듯이 모바일 앱 개발에 빠져들고, 모든 걸 앱으로 개발하고자 하고 했던 미쳐돌던 시절의 이야기다.

요즘은 이미 뻔히 아는 사실이지만, 모바일 앱은 앱 하나당 하나의 기능만 제대로 지원하면 된다. 앱 하나가 이 기능 저 기능 다 가지고 있을 필요는 없다. 예를 들어, 배달의 민족의 경우에는 배달 기능 하나에만 집중한다. 바코드 인식 프로그램들은 바코드 인식해서 내용만 잘 보여주고 하면 된다.

그런데 저때 당시에는 그냥 이것저것 다 집어넣고 해서 일부러 쓸데없이 앱 사이즈를 막 키우고 했던 앱들이 많은 거 같다. 윈도우 프로그램 개발하던 것과 같이, 그냥 자기 회사의 기능이면 이것저것 막 집어넣고 했던 그런 느낌. 자기들 앱이 모든 걸 다 지원해줍니다 같은 형식으로 마구잡이로 집어넣고 했던 그런 기능들이 엄청났다. 그래서 앱 하나에 거의 포탈을 방불케하는 여러 기능들을 마구잡이로 집어넣는 그런 앱들도 무지 많이 개발되었다. 당연 높으신 분들의 생각없는 결단에 의한 것이다.

특히, 자기들만의 솔루션이나 사업 아이템이 기존에 있던 업체들은 모바일 앱을 통해서 그 당시에 엄청나게 막 확장해보고 하려고 미친듯이 돈을 써붓고 그랬다. 그래서 보면 특정 기능을 강조한 앱들도 많았지만, 해당 회사를 강조하여 한 앱에 여러 기능들을 무리하게 막 집어넣고 작업한 앱들도 무지하게 많았다. 지금 생각해보면 그런 회사들이라도 개발에는 돈을 많이 쓰려고 노력했던 거 같다. 싸게 들이던 비싸게 들이던 말이지…

근데 나중에 어느 정도 인기가 식고, 모바일 앱들 중에 유명해지거나 좋은 케이스로 소개되고 하는 것들이 선택과 집중에 의해 특정하게 사용자들이 원하는 기능만 충실하게 지원하는 그런 앱들이 유행하고 하다 보니, 그냥 자신들의 솔루션이나 제품에 맞는 기능만 제대로 하면 그 외에는 무리한 확장을 하려 하지 않은 거 같다. 사용자들도 어느 정도 스마트폰의 사용에 대해서 많은 정보를 알게 되고, 그들이 그대로 회사의 직원들이 되고 하다보니 좋고 나쁨이 뭔지 정확하게 알게 된 것이다. 그 뒤로도 막 잡다한 기능을 넣어야 한다 아니다 라고 하는 건 이젠 의미없는 그런 일들이 되어가고, 그때 당시에 개발했던 수많은 것들은 그냥 잡 노가다가 되어버린 그런 분위기이다.

기존 경험 이야기이다. 당시 했던 잡다한 것들보다 결국은 그 회사의 솔루션을 모바일에 확장시켰던 기능 하나 제외하곤 다 쓸모없게 변해버린 것들을 보니 여러모로 씁쓸해서 적어봤다. 이런 걸 바로 무리한 확장 실패라고 여긴다. 당시에 모바일이 확 유행하면서 모바일도 제공한답시고 해서 제대로 자기들의 솔루션만 제공해서 이미지를 얻는 것만 해도 대단한 것 같다. 반대로, 무리하게 쓸데없는 기능들을 여러모로 확장하고 하면 그 쓸데없는 기능에 대해서는 별 볼일 없는 그냥 잡다한 기능밖에 되지 않는다는… 오히려 그 기능을 전문으로 하는 회사들의 솔루션에 밀려서 아무 쓸모도 없는 그런 기능들이 되곤 했다.

그런 것들 싹 다 지나가니, 결국은 자기들이 원래 제대로 하던 사업으로 돌아가게 된다. 개발자들한테는 개발 경험과 함께 사업과 프로젝트에 대해서 여러모로 돌아보게 되는 좋은 기회였다고 본다. 어정쩡한 개발보다는 선택과 집중이 중요하다는 걸 그때 제대로 보여준 거 같아서…

그리고, 지금 또 그런 부분이 될만한 기술이 충분히 있는데… 그거 잘못까면 어떤 ㅄ업자들이 “암호화폐 꼭 필요한 거거든요! 블록체인하고 분리해서 생각 못하거든요!” 하면서 날 토론장에 몰아넣겠지…

관리자의 프로그래밍

굉장히 안타까운 걸 너무 많이 봐서… 좀 착각하시는 분들이나 이런 상황에 못 헤어나오는 분들을 위해서 글을 적어봅니다. 내용은 지극히 제 주관적일 수 있지만, 한번쯤은 좀 생각해 볼 만한 이야기 같아서 적어봅니다. 딴지 걸고 싶은 분들이나 의견 있으신 분들은 댓글 달면 제가 댓글 내용 승인해서 내용 공유를 해보겠습니다.

개발자들이 흔히들 말하는 것 중에 이런 이야기가 있다. 바로 “백발까지 프로그래밍 하고 싶다.”라는 것이다. 개발이 너무 좋아서, 갈때까지 가보고 싶어서 다들 이런 소리 하나씩은 하고, 한번쯤은 꼭 품에 안고 간다.

근데 지금, 이 때까지 관리자나 팀장 자리에 있으면서도 불구하고 사원, 주임급 개발자랑 동등한 수준의 코딩을 계속해서 하고 나가면서 팀 관리는 팀 관리대로 요구받는 개발자들을 많이 볼 수 있다. 근데 이분들이 하는 방식이 내가, 우리가 아는 그 끝까지 프로그래밍 하는 그런 모습이었을까란 생각이 들었다.

관리자라고 해서 바로 관리자가 되지 않는다. 초반에는 프로그래머, 혹은 코더로부터 시작해서 실력을 쌓고 경력을 쌓아가다 보면 어느 샌가 진급을 하게 되고, 프로젝트 단위로 볼 수 있는 프로그래머로 성장하게 된다. 그렇게 되다 보면 관리자 자리에 앉아 있게 된다. 문제는 여기서부터 시작된다.

관리자가 되면서 코딩을 완전히 손을 떼는 개발자들도 생기고, 그렇지 않는 개발자들도 생긴다. 관리자가 되면서 손 떼는 경우에는 여러모로 말이 많기도 해서 안좋은 경우가 엄청나게 많이 나열되어 있다. 지시를 해야 하는 젊은 개발자들과 개발에 대해서 말이 조금씩 안통하기 시작한다던가, 최신 개발 환경에 대해서 어두워지는 경우가 생기기도 하고 개발 문제 시 생기는 부분에 대해서 이해도가 떨어지는 등 여러모로 골치아픈 경우가 많이 생기게 된다. 따라서 관리자라 하더라도 프로그래밍을 어느 정도 해야 한다는 것은 여러모로 많이 퍼져있다. 필자도 이 부분에 대해서는 찬성을 하는 부분이다.

자, 이제 관리자도 개발을 한다고 치자. 그러고 보니 이젠 관리자한테는 업무량이 배로 늘게 되었다. 개발도 해야 하고, 팀 관리를 위한 관리자의 업무도 주어지게 된다. (팀 관리가 얼마 안될꺼라고 생각하는가? PM의 일이 얼마 되지 않을꺼라고 생각하는 사람들은 개발 경험이 전무하다거나 학생 수준이라고 보겠다.) 이런 상황에서 팀장에게는 선택지가 주어지게 된다. 바로 본인의 참여도를 조정하는 정도에 따라 어떤 방식으로 일을 하냐를 선택할 수 있다.

내가 바라보는 문제가 이제 여기에 발생한다. 개발을 자기 팀원들과 똑같은 수준으로 하게 되다보면 관리자 업무를 야근으로 땜빵하는 관리자들이 존재하게 된다. 의외로 손이 모자란다는 회사들에서는 관리자 분들이 똑같은 수준으로 개발 일정을 본인 것까지 잡고 관리 업무에 대해서는 야근을 하는 등의 땜빵으로 처리하는데, 이렇게 되면 관리 업무에 있어서 본인의 관리가 소홀해지는 경우가 생기는 것이다. 즉, 팀원들에 대한 관리는 하면서 정작 이분들 스스로에 대한 관리기능이 전무한 상태가 되는 것이다.

일단, 관리자들은 프로그래밍을 어느 정도까지 할 수 있어야 하는지를 묻는다면, 난 현업에 투입되어소 아무 막힘 없이 개발할 수 있는 실력이 있어야 한다고 주장한다. 그러나 이런 실력이 있다고 해서 그래도 투입되어서 개발하는 게 좋다고는 생각되지 않는다. 오히려 몇몇 파트를 제외하고는 개발에는 깊게 들어가면 안된다고 본다.

필자인 내 주장으로는 우선 팀장은 팀원들의 진행상황을 확인하고 관리해야 한다. 그리고 그에 따른 리스크 또한 계산해 둬야 하고, 해당 코드들을 결합하는 작업에 대해서 들어가는 것이 맞다고 본다. 그 중에서도 심각하게 생각하는 것이 바로 프로젝트의 문제, 즉 이슈에 대한 대응과 그에 따른 리스크 관리에 대해서 심각하게 보고 있다.

팀원들의 수준은 여럿 존재할 것이다. 주니어 개발자부터 시작해서 시니어, 어드밴스까지 다양하게 존재하는 개발자들이 개발을 진행하면서 서로의 문제를 공유하고 그에 따라 그들이 쉽게 해결할 수 없는 문제가 발생할 것이다. 이것은 그대로 이슈화 되고 리스크로 이어진다. 이럴 때, 관리자가 들어가서 개발을 하거나 하면 맞다고 본다. 특히, 문제 해결을 위한 능력은 아무래도 관리자가 좀 더 경험에 의한 해결을 더 할 수 있기 때문이다. 경력 있는 개발자들이 알고리즘 문제를 풀거나 문제 해결능력을 요구받는 것은 바로 이런 부분에서 조금이나마 경력 있는 사람들이 그렇지 않은 사람들과 해결 방법이나 능력이 다르기 때문인 것이라고 이해한다. 관리자의 조언 혹은 관리자가 생각하는 해결책을 제시해 줌으로써 해당 팀원 개발자가 그 내용을 이해해서 오류나 이슈를 수정할 수 있다면 확실히 처리할 수 있게 되는 것이다.

이때, 관리자에게도 문제 해결을 위한 어느 정도의 프로그래밍 실력(프로그래밍 언어의 스킬과는 관련이 있긴 하지만 많다고는 생각하지 않는다.), 문제 해결 능력이 없다면 여기서 생긴 이슈는 상당히 위험한 이슈가 되는 것이다. 팀원이 여럿 달려들어서 해결해야 하는 문제가 되는 것이다. 이런 이슈가 발생하면 이 이슈를 해결하기 위한 시간과 인력이 필요할 것이고, 이는 또 관리와 보고의 대상이 된다. 프로그래밍을 할 줄 알고 문제 풀이 능력이 되는 관리자라 해도 이런 일이 생기면 또 다른 관리 항목이 늘어나게 되는 것이다.

이런 상황에서 만약 관리자가 팀원들과 동일한 수준으로 개발 스케쥴을 잡고 개발을 진행한다? 매니지먼트에 대해서 바로바로 대응 못하고 뒤에 남아 야근으로 몰아서 관리한다? 관리자의 입장에서는 언제 터질 지 모르는 리스크를 안고 가는 것이라고 생각한다. 팀원 개발자들이 뭐 슈퍼천재라서 뭐 개발만 하면 아무 문제없이 전부 다 되고 그러면 모를까, 현실에서는 그럴 일은 없다.

여기에 회사에서 팀 평가 방식으로 한국식 평가지표들을 끼고 들어서면 그때는 진짜로 미친다. 특히 숫자로 제시하라 뭐하라 그러면서 KPI 지표 만들어서 매달 결과 보고 하거나 하면 어차피 진행사항이 최고 100%까지 밖에 안되는 개발 업무상(참고로 영업은 팀에서 잡은 영업 목표치보다 영업을 더 하면 저 지표가 100%를 초과할 수 있다.) 이런 저런 리스크 다 끌어안고 문제 터질 때마다 밀리고 밀리면 당연히 지표 떨어져서 한 90, 80% 이렇게 되면 일 못하는 개발팀이란 형식으로 또 찔리고 욕먹는 것이 개발팀이 되는 것이고, “개발의 특수성”이란 걸 주장하면 변명 취급당하고… 그냥 악순환이다.

이런 것 없이 그냥 개발자들의 개발 지표만 가지고 따로 관리해주면서 개발에만 집중시켜 주는 회사라면 관리자라 해도 그냥 개발자들처럼 직접 개발해도 될 지도 모르겠다. 그런데, 실제로 대부분의 회사들은 그렇지 않다. 심지어 실리콘벨리에 있는 회사들도 관리자에게는 관리자가 해야 할 일이 있는 것이기에 주니어, 시니어 프로그래머들처럼 프로그래밍 작업에 몰두하고 하는 것이 바람직하다고는 하지 않는다. 그러나 이곳도 현실적으로 막 닥치고 하면 관리자도 개발해야 한다. 그렇기에 관리자도 바로바로 투입 가능한 수준으로 프로그래밍을 할 수 있어야 한다. 근데 첨부터 관리자조차 전부 개발하는 데 인력을 투입하고 관리에 대해서 소홀하다면 그건 좀 고민해야 할 부분이라고 생각한다.

117 – make 규칙 1: 암시적 규칙

make 파일을 작성할 때는 컴파일에 필요한 각 처리 단계와 의존성을 분명히 지정해주었다. 이와 같이 make가 해야 할 일을 분명히 지정하는 것을 명시적 규칙(explicit rules)이라고 한다. 이와 반대로, make 내에 미리 정의된 규칙을 이용해 make 파일을 단순화시키는 규칙을 암시적 규칙(inference rules)이라고 한다.

이를 보여주기 위해서 전에 작성하던 make 파일을 상당히 줄여서 예시로 보여주겠다.

소스 파일에서 오브젝트 파일로 컴파일하도록 하는 단계를 빼고 그냥 바로 test를 오브젝트로 만들어서 빌드하도록 하였다. 전에 글들의 코드를 보고 비교하면 상당히 단축시킨 것을 볼 수 있다. 이 코드는 실행하면 그대로 컴파일 된다. 그 결과가 아래의 화면이다.

대상이 되는 test를 생성하기 위해 make는 의존하는 파일들을 살필 것이다. 그러나 대상이 의존하는 test1.o test2.o test3.o 이 세 파일이 존재하지 않기 때문에 make는 다시 저 파일들의 대상으로 있는 곳을 찾으려 한다. 이때, 찾지 못할 경우에는 오류를 내고 중단할 것 같지만, 실제로는 암시적 규칙에 따라 오브젝트 파일을 gcc가 생성할 수 있는 것을 알기 때문에, test1.o test2.o test3.o 파일을 생상하기 위해 의존하는 파일을 스스로 찾고 컴파일러 호출까지 알아서 수행한다. 이때, 실행 결과에 찍힌 규칙을 보면 알겠지만 gcc를 호출하는 것이 아니라 cc 컴파일러를 호출하였다.

이 과정을 보고 싶다면 make -p 라고 해서 -p 옵션을 통해서 볼 수 있다. 실행하면 아래와 같이 쭉 나온다. 아래 화면에 스크롤 바를 주목해라. 내용 장난 아니게 많다. 암시적 규칙으로 어떤 것들이 불러지는지 알고 싶다면 계속 스크롤을 내려서 살펴보는 것도 좋을 것이다. 근데 당장은 이해 못한다.

진짜로 평가받아야 할 것을 착각하지 말자

매년 오는 시기다. 정시 원서 넣기까지 끝났고, 수시도 지났고, 취업도 어느정도 지났다. 그러다보니 이젠 자기합리화를 하는 친구들이 점점 늘어나는지… 매년 같은 소리 또 나오고 나온다. 여기에 필자가 대학원 졸업하고 취업 준비한다고 이력서 쓰면서도 아직 취업 못한 몇몇 친구들이나 후배들의 이야기랑 비교하면 필자는 진짜 “변종”이기 때문이다.

개발자 된답시고 3학년 쯤 되다보면 다들 자격증 따기 바쁘다. 주로 남들하고 차별받기 위해서라면서 따는 것이 누구나 다 똑같은 생각하면서 따는 CCNA, CCNP라던가 OCP라던가… (솔직히 시험볼 때는 그냥 죄다 덤프 외워서 시험보고 합격하는 게 대학생들의 방식이더라.) 프로그래밍 능력을 증명하기 위해 비전공자들도 정보처리 기사를 취득하고…

근데 진짜로 웃긴 사실은, 실제로 정말 유명한, 실력 있는 기업들이 하는 코딩면접 같은 것은 문제 풀이 능력을 보고자 하는 건데 이런 건 자격증하고는 정말 상관도 없다. 직접 해보면서 익힌 문제풀이 방법이 있을 것이고, ACM 알고리즘 공부 열심히 해서 경진대회 나가보기도 했던 애들이 그런 건 더 잘 푼다. 오히려 그런 애들치고 하나쯤은 제대로 개발해보고 했던 것들도 있을 것이다.

필자가 싫어하는 소프트웨어 마에스트로나 삼성 소프트웨어 맴버쉽이던 그거 있으면서 자기가 직접 참여한 프로젝트가 있을 것이고, 그걸 위해서 뭔가 머리 싸메고 했던 경험이 있을 것인데, 오히려 지금 그런 경험 더 좋아한다. 아님 혼자서나 다른 친구들하고 직접 개발하면서 소스코드 막 짜고 고민해서 정보 남기고 뭐 했던 것 한번 딱 보여주면? 자격증만큼 좋은 거 아닌가?

왜 이런 소리 떠드냐면…

프로그래머에게 자격증은 모욕이다라는 오래된 칼럼의 링크가 뭐 학교니 자격증이니 실력이니 하는 글에서 답답한 사람들이 댓글에 달아주면서 또 다시 세상에 나온 것 땜에 좀 한심해서 적어봤다. 저거 작성된 때가 2014년인데, 아직도 이런다. 진짜 제대로 프로그래머들이 인정받기 위해서는 프로젝트 경험이랑 문제 풀이 능력으로 인정받아야 한다고 하는 게 대체 몇 년간을 계속 떠들어야 제대로 될까란 생각이 든다.

네이버나 다음, 그리고 좀 제대로 된 개발 회사들은 이런 것에 변화해 가는 거 같은데, 그 흔하디 흔한 대부분의 기업들은 아직도 숫자로 표현 잘 되는 녀석으로 줄세우기를 해야 쉽게 풀리나보다. 그러니 어정쩡한 학교 순위 메기기 이야기나 자격증 갯수나 자격증 위치 같은 것에 대해서 목숨 거나보다.

그런 분들에게 반말의 말투이지만 묻고싶다.

“그렇게 일일이 하나하나 다 따지고 자리 잡으니 행복하니? 오히려 개발 일 자체가 재밌지 않을텐데…?”

그나마 그 속에서 재미를 느꼈다면 오래 가겠지만, 그렇지 않다면 그만 두거나 아니면 기계처럼 코드몽키가 되어가겠지…

116 – make 매크로 수정

이미 정의된 매크로 부분을 다음과 같이 바꿔봤다.

OB 매크로에 test3를 추가하는 형태로 OBJF를 바꾼 것이다. 그러면 일전에 모든 것을 다 선언했던 매크로와 동일한 효과가 있다.

그렇다면 이제 프로그래밍 좀 해본 분들은 한 번 정도는 생각해 볼 만한 것이 바로 변수에 변수 뒤엎어 쓰듯이 매크로도 그런 식으로 쓸 수 있지 않을까 하는 생각을 할 것이다. 예를 들어 이렇게 말이다.

확대해서 보일 수 있도록 캡쳐를 하였다. 위의 화면과 같이 하면 되겠지? 라는 생각들 할 수도 있다. 결론부터 말하면 안된다. 아래처럼 된다.

매크로가 정의되는 방식에는 두 가지 방식이 존재한다. 재귀 확장형 매크로와 단순 확장형 매크로가 존재하는데, 우리가 지금 이용하는 make의 경우에는 재귀 확장형 매크로이다. 따라서 이미 선언한 OBJF에 대해서 불러오는 부분에서 이용한 $(OBJF) 부분에서 재귀 콜이 불러져서 다시 참조하는 형태, 즉 다른 매크로를 포함하면 같이 확장을 하는 형태로 만들어졌기 때문에 오류를 낸 것이다. 똑똑한 매크로 실행기가 먼저 알아서 멈춰진 것이다.

그럼 이걸 단순 확장으로 만들 수 없는 걸까? 당연히 있다. 단순 확장 매크로는 말 그대로 단순히 확장만 되는 형태로 만들어지고, 매크로의 정의 차이에 따라서 이걸 판단하게 된다. 우선 단순확장에 대해서만 살펴보려고 하는데, 단순 확장의 경우에는 :=를 통해 확장하면 된다. 아래처럼 해주면 된다.

간단하다. 설명만 힘들 뿐이다. (ㅠㅠ) 이를 실행해보면 오류 없이 진행될 수 있는 것을 볼 수 있다.

그렇다면 기존에 있는 내용에 대해서 치환할 수 있는 매크로도 존재한다. 기본식이 다음과 같다.

$(M_NAME:old=new)

old가 기존에 있던 부분이고, new가 치환하려는 부분이다. 이건 예시 코드를 만들어서 보여주겠다. OBJF에 있는 .o 확장자를 .c 확장자로 변경해 보겠다.

위의 화면과 같이 SRCS라고 하는 소스코드 파일들을 매크로 선언으로 하였다. 그 다음에 이걸 확인하기 위해서 명령어를 하나 추가해서 보여주려고 한다. 아마 오류가 날 코드이다.

실제 실행한 화면이다. 예상대로 오류가 났다. (이거에 대해서는 나중에 글을 추가하겠다.) 그러나, 실행하려는 구문에 보면 뒤에 확장자가 .c로 바뀐 것을 볼 수 있다.

115 – make의 내부 매크로

앞에서 살펴본 매크로의 경우에는 사용자가 원하는 대로 맞출 수 있는 매크로이기 때문에 사용자 정의 매크로라고 한다. (이 표현은 프로그래밍 언어 관련된 내용을 많이 보다보면 중복되게 나오는 표현이라 익숙할 것이다.) 그러나, make 파일을 만들 때 이용할 수 있도록 미리 정의된 매크로들이 존재하는데, 이를 내부 매크로라고 한다. 내부 매크로의 종류와 의미는 아래와 같다.

  • $@ |현재 목표 파일의 이름
  • $* | 확장자를 제외한 현재 목표 파일의 이름
  • $< | 현재 필수 조건 파일 중 첫 번째 파일 이름
  • $? | 현재 대상보다 최슨에 변경된 함수 조건 파일 이름
  • $^ | 현재 모든 필수 조건 파일들

이 내부 매크로를 이용하여 앞에서 살펴본 예제를 더 간단하게 만들어보자.

이 표현이 익숙하다면 상관 없겠는데 필자의 경우에는 은근 싫어하는 스타일이라서 사용하진 않는다.

114 – make와 매크로

make 파일을 작성하다 보면 같은 파일 이름을 여러 번 써야 하는 경우가 있다. 이를 매크로를 사용하면 편리하고 명령어를 단축시킬 수 있는다. 매크로는 다은과 같이 정의하면 된다.

M_NAME = value

사용자가 임의로 정해서 쓰는 매크로 이름인 M_NAME은 등호 오른쪽의 값으로 확정되면 다음과 같은 형태로 이용할 수 있다.

$ (M_NAME)

매크로를 이용하면 복잡한 구문을 간단한 단어로 표현할 수 있으므로, 짧은 make 파일을 만들 때보다는 더 복잡한 파일을 작성할 때 유용하다. 그리고 매크로 이름은 대소문자 모두 가능하지만 코딩 규칙 상 대문자만을 사용하는 것을 일반적이라고 한다. 그리고 make 파일의 상단에 미리 정의한 다음에 이용한다.

매크로를 이용하여 이전에 예시로 보여주기 위해 작성하였던 make 파일을 좀 더 단축시켜보았다. 그 결과가 아래의 화면이다.

113 – make 내용 추가

앞에 장에서는 긴 내용이긴 하지만 make에 대한 기본적인 것들을 다뤘다. 이 내용에서는 앞의 장의 내용을 한번 실습해 본 상태에서 make에 대한 기타 내용을 추가하여 설명하려고 한다. 이 내용들은 make 파일을 만드는 데 있어서 부가적인 내용들이지만, 소스코드가 많아지고 makefile의 양이 많아지고 할 경우에는 유용하게 쓰이는 내용들이다. 또한 현재 C, C++ 프로젝트 중 규모가 거대한 프로젝트에서는 이 내용들이 많이 이용될 것이니 좀 내용이 길고 힘들더라도 이것까진 제대로 익혀두자.

  • 가짜 대상

make 파일의 대상에는 실행 파일과 오브젝트 파일 뿐 아니라 사용자가 임의로 정한 레이블 이름이 올 수 있다고 하였다. 이를 구체적으로 살펴보기 위해서 아래와 같은 make 파일을 작성하였다.

중요한 것은 여기에 있는 바로 clean 레이블이다. clean이라는 대상은 실제 파일에 대응하지 않는 형태의 레이블이다. 이를 두고 가짜 대상이라고 하는데, 이런 가짜 대상은 사용자가 원할 때 make가 특정 명령을 실행하도록 하기 위해 만들어 사용한다.

그리고 clean은 의존성을 갖지 않기 때문에 make는 clean 대상을 만날 때마다 대상이 항상 최신으로 업데이트 된 상태로 간주한다. 이 대상을 구성하려면 다음과 같이 실행해야 하는데, 다음 결과를 보면 알 수 있듯 clean을 대상으로 삼는 명령이 실행된다.

  • 주석

주석은 개발자와 다른 사람이 코드를 이해하는 데 도움이 많이 된다. 필자의 경우에는 주석을 엄청나게 이용하는 편이다. 주석문을 삽입하면 추후에 자신이 작성한 코드를 수정하고자 할 때 훨씬 쉽게 수정할 수도 있고, 다른 사람들이 작성한 코드도 주석문의 내용을 통해 쉽게 이해할 수 있기 때문이다. 그래서 make 파일에도 주석을 이용하여 작성할 수 있는데, #이 앞에 있으면 그 뒤의 문장은 주석이 된다. 단, 프로그램 코드의 주석과 달리 명령 라인 뒤에 주석을 붙이지는 않는다. 셸이 #에 대해서 메타 문자 형태로 인식하여 이것을 변환하려고 시도할 수 있기 때문이다. 리눅스를 쓰는 친구들은 configure 파일에도 보면 주석을 작성하는 데 옵션 뒤에 주석을 쓰는 것을 보지 못했을 것이다. 이와 같은 원리로, 따라서 주석은 코드와 전혀 관계 없는 곳에 작성한다.

  • 자동 의존 관계 생성

의존 관계의 경우, gcc에서 자동으로 생성할 수 있는 방법이 있다. gccmakedep 명령어를 이용하면 의존 관계를 자동적으로 생성할 수 있다. 그런데, 이 작업을 거치기 전에, 우분투 사용자들은 다음과 같은 명령어로 패키지를 하나 설치해야 한다.

sudo apt-get install xutils-dev

이 작업은 gcc의 기본 환경인 make에 있는 것이 아니라 imake라고 별도의 패키지에 있는데, 이걸 포함하고 잇는 것이 바로 저것이다. (페도라 계열을 그냥 바로 gccmakedep을 설치하면 알아서 저걸 깔아준다.) 그러지 않으면 오류가 난다.

그럼 의존성은 어떻게 자동으로 만들 수 있는지 확인해 보겠다. 우선 전에 이용하던 make 파일을 수정한다.

dep이라는 가짜 대상을 만들고, gccmakedep 명령으로 세 코드의 의존성을 생성하도록 하였다. 그리고 저장 후 빌드를 할때, 아래와 같이 나오면 위의 패키지가 설치되어 있지 않은 것이다.

아래와 같이 나와야 정상적으로 끝난 것이다.

그리고 나서 Makefile을 열어 보면 아래와 같이 작성되어 있는 것을 알 수 있다.

주석으로 DO NOT DELETE라고 적힌 곳부터가 자동으로 생성된 코드이다. 각각 사용자가 작성한 헤더인 a.h 말고도 다른 것들이 더 있는데, 저것들이 a.h 안에 삽입되어 있는 stdio.h와 연관된 의존 헤더들이다. 현재 시스템이 어떤 컴파일러, 어떤 라이브러리가 설치되어 있느냐에 따라서 의존된 결과가 다를 수 있다.

  • 긴 명령어 쓰기

바로 위에 그림에 자동으로 생성된 의존 관계를 보면, \로 끝이 끝나는 걸 볼 수 있다. 이것이 바로 명령어가 옆으로 길어져서 다음 라인으로 옮겨 쓰기 위해 이용하는 것이다. \의 뒤에는 아무것도 작성하지 않고 바로 다음 문장으로 넘어간다. 여러 문장으로 하고 싶을 경우에는 끊어야 할 부분에 \를 입력하여 다시 길게 작성한다.

  • 여러 개의 셸 명령 사용

make 파일 내에서는 모든 셸 명령을 사용할 수 있다. 그러나 모든 명령이 다른 셸 안에서 실행되기 때문에 하나 이상의 명령을 사용할 때 문제가 생길 수 있는데, 이를 해결하기 위해서 세미콜론(;)을 이용하여 작성을 한다. 아래와 같이 적으면 오류가 나는 상황인데,

세미콜론을 넣고 한 문장으로 작성하면 된다.

물론, 다음 문장으로 넘겨서 쓰는 것도 가능하다.

이것을 실행해 보면 확실히 test3.o 레이블을 실행할 때, 명령이 두 라인 실행되어서 복사본이 만들어지는 걸 볼 수 있다.

  • 명령 실패 무시

make를 실행하다 보면 make는 대상이 있는지, 대상이 의존하는 파일이 수정되었는지의 여부에 따라 필요한 명령을 실행한다. 하지만, 중간 명령의 실행에 실패하면 오류 메시지를 출력하면서 바로 동작을 멈춘다. 이럴 때, 명령이 실패해도 작업을 계속 진행하고 싶을 경우에는 이 과정을 무시할 수 있어야 하는데, make 파일 내 명령어 앞에 하이픈(-)을 삽입하여 주면 된다. 이것도 예시를 확인하자.

확실하게 없는 파일을 복사하려 하고 있다. 이 경우에 실제로 make를 하게 되면 오류가 난다.

이제, 오류가 나는 줄을 처리하는 곳에 하이픈을 붙여서 실행하면 오류가 나지 않고 마저 실행되지 않았던 명령이 실행될 것이다.

이 결과를 다시 보기 위해서 clean으로 다 지운 후, 다시 컴파일을 시도해 보았다. 그러면 아래와 같이 오류 명령과 함께 무시됨이라고 해서 해당 처리가 무시되었다는 것을 볼 수 있다.

이로써 Makefile을 만드는 데 있어서 가장 기본이 되는 내용들을 전부 살펴보았다. 이 외에도 매크로라던가 사용 규칙, 옵션 들을 좀 더 살펴봐야 하지만, 여기까지만 알아도 공부하는 수준에서의 make 파일의 관리는 될 수 있다. 셸 스크립트 배우는 거 같으면서도 코드 컴파일 및 클리어 기능을 통해서 어느 정도 관리를 할 수 있는 구조가 이런 것이구나를 확인할 수 있는 것은 중요한 요소라고 본다.