<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joonho dev blog]]></title><description><![CDATA[Joonho dev blog]]></description><link>https://joonho.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 30 Aug 2026 22:59:42 GMT</lastBuildDate><atom:link href="https://joonho.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[프랙탈을 보는 그래픽 프로그램 만들기]]></title><description><![CDATA[fract-ol은 반복되는 모양이 나타나는 프랙탈 패턴을 적절한 색으로 표현하여 창에 디스플레이되도록 하는 그래픽 과제이다. 픽셀과 이미지 그리고 이벤트에 따라 이미지를 업데이트하는 그래픽 라이브러리를 다루는 방법, 그리고 fractal이라는 수학적으로 정의된 패턴을 이해하는 것, 이벤트(키, 마우스 휠 등)에 따라 평행이동, 줌, 색 팔레트, 패턴 선택을 처리하는 방법을 배울 수 있었다. 일단 그래픽 과제인만큼, 결과물을 먼저 보자.
http...]]></description><link>https://joonho.hashnode.dev/fractal</link><guid isPermaLink="true">https://joonho.hashnode.dev/fractal</guid><category><![CDATA[beginner]]></category><category><![CDATA[42Seoul]]></category><category><![CDATA[fractals]]></category><category><![CDATA[graphics]]></category><dc:creator><![CDATA[Joonho Lee]]></dc:creator><pubDate>Thu, 10 Aug 2023 15:16:49 GMT</pubDate><content:encoded><![CDATA[<p>fract-ol은 반복되는 모양이 나타나는 프랙탈 패턴을 적절한 색으로 표현하여 창에 디스플레이되도록 하는 그래픽 과제이다. 픽셀과 이미지 그리고 이벤트에 따라 이미지를 업데이트하는 그래픽 라이브러리를 다루는 방법, 그리고 fractal이라는 수학적으로 정의된 패턴을 이해하는 것, 이벤트(키, 마우스 휠 등)에 따라 평행이동, 줌, 색 팔레트, 패턴 선택을 처리하는 방법을 배울 수 있었다. 일단 그래픽 과제인만큼, 결과물을 먼저 보자.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/DWs4lxscp94">https://youtu.be/DWs4lxscp94</a></div>
<p> </p>
<h2 id="heading-1-mlx">1.그래픽 라이브러리(mlx)</h2>
<p>위와 같은 이미지를 화면에 출력하기 위해서는 화면출력의 기본단위인 각 픽셀의 위치와 그 픽셀에 어떤 색을 출력할지, 그리고 그렇게 지정된 픽셀들의 세트인 이미지를 관리하고 이를 실제 os의 window에 표시해주는 라이브러리가 필요하다. 이번 과제 요구조건에 따라 42 커리큘럼에서 사용하는 mlx 라이브러리를 사용했다.</p>
<p>그동안 어렴풋이 짐작했던 컴퓨터 그래픽, 특히 화면에 화려한 그래픽을 출력하는 매우 기초적인 과정이 어떻게 작동하는 지를 한땀한땀 단계별로 배울 수 있는 라이브러리라 생각한다. mlx 라이브러리에서는 mlx를 초기화하고, 생성할 윈도우의 크기를 지정하고, 여기에 출력할 이미지를 생성하고 그 이미지의 x,y 번째 픽셀에 지정한 색을 rgb형식으로 지정하고 이 이미지를 생성한 윈도우에 출력하는 과정을 통해 위와 같은 컴퓨터 이미지가 화면에 출력된다.</p>
<p>또한 키보드 키를 누르거나 놓는 이벤트, 마우스 버튼을 누르거나 놓는 이벤트, 마지막으로 창의 종료버튼을 누르는 이벤트가 발생했을 때 호출할 event hook을 작성하여 해당 이벤트 발생시 위의 영상처럼 색의 종류를 변화시키거나, 줌인 줌아웃, 이동 등을 처리할 수 있었다.</p>
<p>이전까지도 c library의 malloc 등의 외부함수를 사용했지만 이렇게 여러가지 외부함수를 사용할 수 있는 첫 과제라 이 라이브러리로 목표로 하는 동작을 처리하기 위해 어떻게 사용해야하는지 익히는데 상당한 시간이 소요되었다. 특히 mlx는 42 교육과정에서만 쓰이는 것으로 추정되는 라이브러리인데 manual도 부실하고 (linux 버전을 설치하면 볼 수 있다), 검색해서 찾을 수 있는 레퍼런스도 부족하여 적응하는데 쉽지 않았다. 하지만 그만큼 교육적으로 로우레벨에서 어떻게 이미지와 픽셀을 처리하고 이를 화면에 출력하는지를 배울 수 있도록 설계된 것을 느낄 수 있었다.</p>
<h2 id="heading-2">2.프랙탈 패턴</h2>
<p>처음 프랙탈을 화면에 출력한다 했을 때는 막막했다. 검색해서 나오는 <a target="_blank" href="https://en.wikipedia.org/wiki/Fractal">fractal</a>들의 이미지를 어떻게 구현하나 싶었는데 과제에서 요구하는 mandelbrot이나 julia set의 경우 수학자들이 찾아놓은 결과물을 구현하는 것이라 생각보다 어렵지 않았다.</p>
<p>간단하게 말하자면 복소평면 위의 점 z(x + yi)를 각 세트에서 주어진 방법으로 여러번 mapping하여 나오는 z_i 값이 몇번의 반복연산(합성함수라 생각하면 편할 것이다)을 거쳐 발산하는 경계를 넘어가는지를 계산하고 이 반복횟수가 낮으면 쉽게 발산하는 부분으로 해당 복소평면을 밝은 색으로, 반복횟수가 높으면 많은 연산을 하는 부분으로 해당 복소평면을 어두운 색으로 칠하게 되면 그 경계가 계속 확대했을 때 동일한 모양이 반복하여 나타나는 성질이 있다.</p>
<p>이 반복하는 연산에서 z -&gt; f(z)의 연산을 어떻게 정의하는지에 따라 그 발산과 수렴의 경계의 모양이 다르게 나타나고 각각의 식을 발견한 사람의 이름을 따 Mandelbrot, julia set이라 한다. 복소평면과 맵핑, 그리고 무한 수열의 수렴 발산 등의 개념은 나에게 친숙한 것이라 수월하게 이해할 수 있었다.</p>
<h2 id="heading-3">3.이벤트 처리(평행이동, 줌인, 줌아웃)</h2>
<p>w,a,s,d 키에 따라 화면을 상하좌우를 표시하도록 이동시키고, 마우스 휠 입력에 따라 줌인 줌 아웃하는 것이 주요 기능이었다. 이를 수월하게 처리하기 위해 화면에 표시할 복소평면의 범위를 x_min, x_inc(픽셀당 증가하는 x값), y_min, y_inc로 저장해놓고 적절한 수치(현재 화면에 표시되는 폭이나 높이의 0.05배, 즉 5%씩 증감하여 표시하도록 했다.</p>
<p>또한 줌인 줌아웃의 경우 처음에는 원점을 기준으로 폭이나 높이의 10%씩 줄이거나 늘리는 방법을 사용했는데 보너스에서 커서의 위치를 중심으로 줌인, 줌아웃을 요구했다. 이는 단순하게 커서가 위치한 픽셀이 나타내는 복소평면의 x,y좌표가 줌인, 줌 아웃 이후에도 동일한 x,y좌표를 동일한 픽셀에 표시하도록 하면 되었다. 평행이동-&gt;스케일링-&gt;평행이동으로 처리해도 되었지만 그냥 invariant개념을 이용하여 식을 간결하게 정리하여 계산하도록 하니 마우스 스크롤을 확 당겨도 조금 더 부드럽게 움직이는 것을 볼 수 있었다.</p>
<p>수학적인 배경이 없다면 어려울 수 있는 부분이지만, 오래되었더라도 물리학과에서 공부했던 좌표계 변환이었던 만큼 쉽게 처리할 수 있는 기능이었다. 위의 수치를 결정하는데 고민이 있었지만 오랜기간 그래픽 소프트웨어를 개발한 adobe의 프로그램들의 해당기능의 값들을 대략적으로 직접 찾아보고 그와 비슷하게 설정하니 자연스럽고 익숙한 상하좌우 이동, 줌인 줌아웃을 구현할 수 있었다.</p>
<h2 id="heading-4">4.색 팔레트와 컬러 스페이스</h2>
<p>이전까지 RGB로 색을 표현한다는 것을 아마 red, green, blue의 중첩으로 계산하겠지라고 알고 있었다. 프랙탈 위키에 나온 알록달록한 색으로 표현하는 방법을 검색하여 그래픽에서 사용자가 어떻게 색을 인식하는지에 따라 다른 파라미터로 결정되는 컬러스페이스라는 개념을 배울 수 있었다.</p>
<p>Red, green, blue 색의 빛의 세기에 따라 색을 결정하는 simple RGB, 망막의 광수용체의 파장범위에 따라 조정된 xyz colorspace, 그리고 hue, chroma, luminance, 한국어로 하면 색조, 밝기, 휘도(채도?)로 번역되는 미술에서 사용되는 색의 성질의 값으로 색을 표현하는 HCL colorscpace가 있다는 것을 배울 수 있었다. 정말 그래픽이라는 분야가 미술과 광학, 그리고 시신경의 수용체까지 많은 분야의 지식이 모여 발전해왔음을 알게 되었다.</p>
<p>많은 그래픽 라이브러리에 이를 변환하는 함수 들이 있지만 mlx에는 없어 해당 변환 매트릭스를 사용하여 직접 프랙탈의 iteration-&gt;HCL-&gt;RGB로 색을 이미지로 변환해서 출력해보았다. 하지만 여기에 사용되는 행렬 연산이 많아 이벤트에 따른 이미지 처리속도가 더 떨어짐을 발견하였고 해당 matrix와 매핑을 근사하여 줄이고 줄였더니 연산량이 줄어들고 속도가 다시 회복되었다.</p>
<h2 id="heading-5">5.동료 평가</h2>
<p>현재(cpp04)까지 받은 수많은 평가 중에서 유일하게 공통과정인 이너서클을 완료한 멤버에게 평가를 받은 과제가 이 프랙탈이었다.</p>
<p>숫자 인덱스를 지정해서 쓰는 것이 익숙해져 있었는데 enum이나 매크로를 활용하는 것이 가독성 향상에 좋다는 점, mlx와 같은 외부라이브러리의 경우 어떻게 작동하는지를 문서 등을 통해 더욱 정확하게 알고 사용하는 것이 좋다는 점(mlx ptr 반환, img 수정), 줌인 줌아웃에서 내가 생각했던 이론적이 한계가 아닌 사용자가 다시 줌인할 수 있는 한계를 고려하는 점, 협동작업을 위해 파일명을 더 직관적으로 작성하여 관리하는 점 등 수많은 건설적인 피드백을 받았다.</p>
<p>파일명, enum 처리등은 바로 다음과제인 minishell에서, 그리고 수많은 mlx 에러 처리, 사용자 중심으로 생각하여 구현하는 방법은 miniRT에서 적용하며 코드 가독성, 작업 효율, 프로그램의 편리성 등을 발전시킬 수 있었다.</p>
<p>또한 동료평가, 코드리뷰의 중요성을 다시 한번 깨닫고 내가 평가를 받을 때나 하러 갈 때나 조금 더 집중하여 하나라도 더 배울 수 있게 노력하고 있다.</p>
]]></content:encoded></item><item><title><![CDATA[최소 움직임의 개수로 정렬하기]]></title><description><![CDATA[Push Swap은 주어진 숫자의 수열을 최소의 움직임으로 정확히 정렬하는 문제이다. 가능한 모든 조합 중 최소라고 생각한다면 매우 높은 시간복잡도를 가지겠지만 수열의 원소 개수에 따라 과제에서 주어진 가이드라인이 있고 이를 만족하는 방법으로 정렬시켜도 충분하다. 다만 우리가 보통 공부하는 정렬 알고리즘의 시간복잡도는 두 개의 원소를 비교하는 횟수를 최소화하는데 비해 이 과제에서는 정렬시키기 위한 움직임의 규칙이 있고 이 움직임의 횟수를 최소화...]]></description><link>https://joonho.hashnode.dev/sort-by-minimum-moves</link><guid isPermaLink="true">https://joonho.hashnode.dev/sort-by-minimum-moves</guid><category><![CDATA[beginner]]></category><category><![CDATA[42Seoul]]></category><dc:creator><![CDATA[Joonho Lee]]></dc:creator><pubDate>Mon, 24 Jul 2023 23:40:17 GMT</pubDate><content:encoded><![CDATA[<p>Push Swap은 주어진 숫자의 수열을 최소의 움직임으로 정확히 정렬하는 문제이다. 가능한 모든 조합 중 최소라고 생각한다면 매우 높은 시간복잡도를 가지겠지만 수열의 원소 개수에 따라 과제에서 주어진 가이드라인이 있고 이를 만족하는 방법으로 정렬시켜도 충분하다. 다만 우리가 보통 공부하는 정렬 알고리즘의 시간복잡도는 두 개의 원소를 비교하는 횟수를 최소화하는데 비해 이 과제에서는 정렬시키기 위한 움직임의 규칙이 있고 이 움직임의 횟수를 최소화하기 때문에 응용이 필요한 과제이다.</p>
<h2 id="heading-1">1.최초 정렬 방법 선택</h2>
<p>과제 시작 당시 이 과제 평가도 한번 다녀왔고 이 과제에서 효율이 좋은 방법 - 그리디 알고리즘이 있다고 들었다. 실제 내 과제 이후 같이 공부하는 분들의 과제를 같이 고민해보니 구현 난이도가 쉬운 방법이었다. 이를 선택하지 않은 이유로 첫번째, 그리디 알고리즘은 현재 선택이 이후 선택에 영향을 주지 않고, 부분적인 문제의 최적 솔루션의 집합이 최적 솔루션일 경우 최적의 솔루션을 찾을 수 있는데 과제의 문제에서는 두 조건 모두 만족하지 않는다. 두번째, 그럼에도 그리디 알고리즘은 실용적으로 적절한 시간복잡도내에 근사적인 솔루션을 찾는데 유용하지만 과제에서는 시간은 문제가 아니다. 또한 이후 여러 과제결과를 보면 이 과제에서는 그리디가 계산시간도 느리고 결과값(움직임의 횟수)가 크게 적지도 않다.</p>
<p>이후 <a target="_blank" href="https://en.wikipedia.org/wiki/Sorting_algorithm">정렬 알고리즘 위키</a>에서 주요 정렬 알고리즘의 방법을 공부하고 이를 주어진 시간복잡도가 어떻게 나오는지, 그리고 이 과제에 적용했을 때 예상되는 움직임의 횟수를 대략적으로 계산한 결과 병합 정렬의 하이브리드 형태인 tim sort 방법으로 구현하기로 결정했다. 기억하기로 이 최초의 계산 결과 대략 4000개의 움직임이 예상되었고 구현 작업에 들어갔다.</p>
<h2 id="heading-2">2.구조체 설계</h2>
<p>내가 선택한 병합 정렬 알고리즘에 따라 계산할 때 수월한 구조체를 결정했다. 먼저 문제의 제한된 움직임을 표현하기 위한 deque를 공부하고 구현했다. 이후 병합 정렬에서 이미 정렬된 부분수열을 표현하기 위한 subseq 계층을 하나 더 추가했다.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690239491715/b61e6f0d-31a6-47d9-b3f4-6a9a9a01459f.jpeg" alt class="image--center mx-auto" /></p>
<p>위와 같이 각 노드들의 deque, 그리고 정렬된 부분수열의 표현하는 subseq들의 deque를 전체 deque에서 바로 접근할 수 있도록 구현하면 직관적이고 병합정렬에 사용하기 편리한 자료구조를 만들 수 있었다.</p>
<h2 id="heading-3">3.문제 발견과 해결</h2>
<p>위와 같은 방법으로 일단 움직임 개수 최적화는 충분하지 않아도 정렬까지는 완성했는데 이전에 생각지도 못 한 문제점이 발견되었다. 먼저 과제에서 제한된 움직임을 표현하기에는 위와 같은 자료구조보다는 cyclic list가 조금 더 효율적임을 발견했다. head와 tail을 가지는 sequence 형태와는 모든 조건들이나 함수가 달라져야 하기에 고민끝에 현재 자료구조를 그대로 유지하기로 결정했다. (물론, 이후 같이 공부하는 분이 이 과제를 할 때 cyclic list를 추천하고 해당 구현과정을 같이 경험하면서 결국 cylclic 형태도 공부했다...)</p>
<p>이보다 큰 문제는 움직임의 개수가 과제 기준 100% 통과하는 값이 안 나오는 것이었다. 2,3일 정렬 알고리즘 공부하고 계산한 초기 예측이 틀리는 것은 당연하다 생각했다. 그래서 구현이 끝난 5일차부터 약 5일간 어떻게 하면 움직임을 최소화할지 가능한 모든 방법을 고민하면서 최적화했다.</p>
<p>이 최적화 방법에는 1) 기본적인 움직임 연산 합치기, 2) 단순 2개 subseq merge가 아닌 1개의 subseq를 인접한 3방향으로 쪼개 보내는 방법 3) 각 subseq merge에 따른 움직임 횟수가 최소화 되는 각 merge 방향을 점화식으로 계산, 적용하기 등을 모두 했지만 기준 100%에 들어가는 case가 random test중 약 50% 정도까지 도달하지 못 했다. 물론 이 방법들을 추가하기 전에 해당 방법으로 줄일 수 있는 움직임의 기댓값을 대략적으로 계산했지만 복잡도 계산처럼 근사치 계산이라 매번 예측보다 감소량이 적었다.</p>
<p>이때가 한 10일차로 어느정도 지친 상태에서 일단 코드리뷰를 받으면서 해결책을 찾고 있었다. 그 날 점심시간에 같이 공부하던 동료분이 push swap 평가를 다녀와서 반대 방향 3방향 merge가 움직임 갯수가 적다는 결과를 알려주셨다. 내가 만들었던 방법(1 to 3)는 각 단계마다 정렬된 1개의 subseq를 인접한 3개에 병합하는 방법이었고 그 분이 사용한 방법은 3개의 정렬된 subseq를 1개로 병합하는 방법이었다. 이러한 방법도 가능하겠다 생각은 했지만 그동안 만든 코드의 대부분을 수정해야하는 것 때문에 계속 하던 방법에 수정, 추가만 하고 있었는데 이야기를 듣고 계산해보니 약 log3/log2 배수 만큼 움직임이 줄어들 수 있음을 확인했다.</p>
<p>결국 대부분의 코드를 수정하는 대수술을 거쳤으나 이 정렬 방법의 움직임 개수가 적은 부분에서 또 과제 기준 100%를 20%의 랜덤 케이스에서 넘기지 못 하는 문제가 생겼다. visualizer로 확인 결과 불필요한 움직임이 어디서 발생하는지 확인했고 다른 정렬 알고리즘을 예상 움직임 횟수에 따라 사용하는 로직을 추가하여 해결하게 되었다.</p>
<h2 id="heading-4">4. 과제를 끝내고 느낀 점</h2>
<p>결국 100%로 과제를 완료하긴 했으나 그 과정에서의 시행착오를 돌아보면서 이후 과제를 진행할 때 보완해야할 점들을 발견했다.</p>
<ul>
<li><p>결과 예측을 더 꼼꼼히 정확하게 해야한다. 처음에 조금 더 정확하게 예상되는 움직임의 기댓값을 계산했다면 중간의 시행착오 없이 일찍 전체적인 방법을 변경했을 것이라 생각한다.</p>
</li>
<li><p>자료구조 설계 시 불편함을 느꼈다면 바로 수정해야 한다. 이 과제를 할 때는 코드 수정 작업속도도 많고 아직 수정 한번 하면 여기저기 에러가 발생해서 시간 효율을 따져 자료구조 수정을 미루었었다. 하지만 이후 정렬하는 부분을 작성함에 있어서 계속 조건이나 로직이 길어져 그 이상의 문제점을 해결하는데에 시간을 많이 사용했다. 돌이켜보면 불편함을 느꼈을 때 바로 자료구조를 수정했다면 전체 과정에서 작업효율이 늘어났을 것이라 생각한다.</p>
</li>
<li><p>언제 물어봐야 하는 것일까. 이 부분은 아직도 스스로 고민중인 부분이다. 물론 시간적인 제약이 있고 빠르게 해결해야 한다면 해당 문제에 대한 경험이 있는 동료에게 질문하는 방법이 효과적이다. 하지만 내가 스스로 이해하고 해결책을 찾을 수 있는 논리의 복잡도를 증가시키기 위해서는 기초적인 지식을 습득한 후에 스스로 끊임없이 생각하는 과정이 필요하다. 이러한 연습을 통해 이전에는 a-&gt;b-&gt;c-&gt;d라는 논리를 떠올리는데 1시간이 걸리는 것이 10분이 되고 1분이 되고 이후 a-&gt;d라는 논리를 필요로하는 다른 복잡한 문제에 대한 해답을 고민할 수 있는 것이다. 이러한 경험을 많이 했기에 아직 스터디하는 단계 어디에서 질문해야하는지 고민하고 있다.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[가상머신과 서버 만들기]]></title><description><![CDATA[Born2beRoot는 가상머신에 리눅스 운영체제를 설치하고 사용자 관리, ssh를 통한 연결, 주기적인 작업 자동화, 간단한 wordpress 서비스 설치 등을 하는 과제로 서버 설치하는 과정을 공부하는 것이다. 그리고 과제 정리하는 글 작성이 많이 늦어졌다. 앞으로 일주일간 그동안 밀린 과제관련 공부한 내용을 열심히 정리해야겠다.
1.가상머신(virtual machine)
가상머신이란 물리적 컴퓨터로부터 cpu, memory, disk등의 ...]]></description><link>https://joonho.hashnode.dev/virtualmachineforserver</link><guid isPermaLink="true">https://joonho.hashnode.dev/virtualmachineforserver</guid><category><![CDATA[beginner]]></category><category><![CDATA[42Seoul]]></category><dc:creator><![CDATA[Joonho Lee]]></dc:creator><pubDate>Fri, 23 Jun 2023 05:10:32 GMT</pubDate><content:encoded><![CDATA[<p>Born2beRoot는 가상머신에 리눅스 운영체제를 설치하고 사용자 관리, ssh를 통한 연결, 주기적인 작업 자동화, 간단한 wordpress 서비스 설치 등을 하는 과제로 서버 설치하는 과정을 공부하는 것이다. 그리고 과제 정리하는 글 작성이 많이 늦어졌다. 앞으로 일주일간 그동안 밀린 과제관련 공부한 내용을 열심히 정리해야겠다.</p>
<h2 id="heading-1virtual-machine">1.가상머신(virtual machine)</h2>
<p>가상머신이란 물리적 컴퓨터로부터 cpu, memory, disk등의 자원을 할당하여 가상 컴퓨터처럼 작동하는 환경이다. 물리적으로 자원을 할당해주는 컴퓨터를 host, 자원을 할당받아 가상머신으로 작동하는 컴퓨터를 guest machines, 또는 guest라 한다. 위의 자원들을 할당하는 hypervisor 소프트웨어의 타입중 직접 하드웨어 자원을 할당하는 type 1과 호스트 컴퓨터 OS 상에서 자원을 할당하는 type 2가 있는데 과제에서는 type 2에 해당하는 virtual box를 사용했다.</p>
<p>가상머신을 사용하는 이유로는 server consolidation(서버 병합?)이라 한다. server로 사용하는 컴퓨터의 하드웨어 자원에 비해 대부분의 각 서버가 필요로하는 자원이 작아 컴퓨터의 하드웨어자원을 각 서버가 필요한만큼 가상머신을 통해 분배하여 사용하는 것이다. 또한 가상머신의 경우 고립된 환경이라 보안 등 여러 테스트에서 문제가 발생해도 해당 가상머신 범위에 제한되고 이 때문에 고립된 테스트를 위해 사용한다.</p>
<h2 id="heading-2-sudo-password-policy">2. 사용자 관리 (sudo, password policy)</h2>
<p>리눅스에서는 각 사용자에게 필요한 권한을 부여할 수 있는데 각 권한을 가진 group에 개별 사용자를 등록하면 그 사용자가 해당 group이 가진 permission을 획득하게 된다. 컴퓨터의 기본 관리자인 root권한이 필요할 경우 root 사용자의 비밀번호를 공유하지 않고 root와 같은 권한을 부여하는 sudo (super user do) 패키지를 사용할 수 있다. 이 또한 개별사용자를 sudo group에 등록하여 sudo 명령어를 통해 root와 같은 superuser권한을 사용할 수 있게 한다.</p>
<pre><code class="lang-c">sudo command args
</code></pre>
<p>개별 사용자가 필요한 경우 위와 같이 sudo 를 명령어의 prefix로 입력하면 개별사용자의 비밀번호를 입력하고 root에게 제한된 명령어를 실행할 수 있다. 이와 같은 비밀번호 정책이 안전하고 선호되는 이유는 개별 사용자가 root 비밀번호를 임의로 변경하는 것을 막을 수 있고, sudo 명령어를 사용한 개별 사용자와 그 로그를 저장하기 때문에 문제가 발생했을 시 이를 확인하고 해당 유저를 sudo group에서 제거하는 등 대처가 가능하기 때문이다.</p>
<h2 id="heading-3ssh-firewall">3.SSH와 firewall</h2>
<p>SSH 는 secure shell or secure socket shell의 약자로 원거리에서 암호화된 안전한 접근을 할 수 있는 protocol 또는 해당 유틸리티를 뜻한다. 이를 통해 해당 컴퓨터의 실제 위치에서가 아닌 원격으로 필요한 작업을 안전하게 수행할 수 있다.</p>
<p>방화벽과 ssh설정을 통해 ssh접속이 가능한 port 를 지정하고 또 사용하는 port이외의 다른 접속을 거부하도록 설정한다면 더욱 안전한 원격 작업이 가능하다.</p>
<p>기본적으로는 아래와 같이</p>
<pre><code class="lang-c">ssh user@hostname
</code></pre>
<p>명령어를 입력하면 ssh연결을 통해 해당 유저의 비밀번호를 입력 후 원격 접근이 허용된다. 하지만 ssh.keygen으로 생성한 public key를 원격 컴퓨터에 저장할 경우 비밀번호 입력없이 명령어만으로 접속할 수 있다.</p>
<h2 id="heading-4cron">4.Cron - 주기적인 작업 자동화</h2>
<p>동일한 명령어를 주기적으로 실행해야 할 경우 cron package를 통해 이를 자동화할 수 있다. crontab -e를 통해 주기적으로 실행할 명령어의 반복주기, 명령어를 지정할 수 있다. 또한 cron의 경우 background service 이기 때문에 systemctl start로 시작하고 enable로 컴퓨터 부팅시 백그라운드에서 자동 실행되게 할 수 있다. 반대로 systemctl stop 으로 현재 작동을 중지하고 disable로 컴퓨터 부팅시 백그라운드 실행을 중단할 수 있다.</p>
<h2 id="heading-5wordpress">5.wordpress 서비스 설치</h2>
<p>브라우저를 통해 현재 가상머신의 특정포트로 접속할 때 wordpress 서비스를 제공하기 위해서 lighttpd webserver를 설치했다. webserver는 http에 따른 요청을 받아 필요한 스크립트를 보내주는 역할을 한다. 이때 필요한 php처리를 위해 php와 유저 정보등을 저장할 mariadb 데이터베이스를 같이 설치하여 wordpress에 연결하는 작업을 했다. bonus파트이고 필요한 부분만 간단하게 만들었지만 원격 서버를 통해 클라이언트의 요청을 받아 웹페이지를 제공하는 서버를 만들기 위해 필요한 것이 무엇인지를 배울 수 있었다.</p>
]]></content:encoded></item><item><title><![CDATA[Getnextline 만들기]]></title><description><![CDATA[42서울 두번째 과제로 진행했던 getnextline은 '\n'까지의 한 줄을 읽고 이를 출력하는 함수를 만드는 것이다. gets, fgets와 유사하지만 이를 사용하지 못 하고 systemcall인 read함수를 사용해야 하기에 read 함수의 결과값이 어떻게 나오는지를 먼저 확인하고 각각의 경우에 과제에서 요구하는 한 줄 str으로 만들 것인가를 생각하는 순서로 진행했다.
1.Read() function
https://man7.org/lin...]]></description><link>https://joonho.hashnode.dev/getnextline</link><guid isPermaLink="true">https://joonho.hashnode.dev/getnextline</guid><category><![CDATA[beginner]]></category><category><![CDATA[C]]></category><category><![CDATA[42Seoul]]></category><category><![CDATA[static variable]]></category><category><![CDATA[getnextline]]></category><dc:creator><![CDATA[Joonho Lee]]></dc:creator><pubDate>Tue, 23 May 2023 20:35:34 GMT</pubDate><content:encoded><![CDATA[<p>42서울 두번째 과제로 진행했던 getnextline은 '\n'까지의 한 줄을 읽고 이를 출력하는 함수를 만드는 것이다. gets, fgets와 유사하지만 이를 사용하지 못 하고 systemcall인 read함수를 사용해야 하기에 read 함수의 결과값이 어떻게 나오는지를 먼저 확인하고 각각의 경우에 과제에서 요구하는 한 줄 str으로 만들 것인가를 생각하는 순서로 진행했다.</p>
<h2 id="heading-1read-function">1.Read() function</h2>
<p><a target="_blank" href="https://man7.org/linux/man-pages/man2/read.2.html">https://man7.org/linux/man-pages/man2/read.2.html</a></p>
<p>위의 man 페이지에 나와있는 read function 설명도 이제보면 명료하지만 read함수가 낯설었던 과제 당시에는 충분히 이해되지 않아 read의 입출력을 아래와 같이 조사했다.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;unistd.h&gt;</span></span>

<span class="hljs-keyword">char</span> *buffer[BUFFER_SIZE];

check = read(fd, buffer, size);
</code></pre>
<p>파일 또는 stdin인 fd, 모두 '\0'으로 초기화된 buffer[]에 위와 같이 read 함수를 적용했을 때 check(반환값)과 buffer의 변화를 생각할 수 있는 경우의 수로 나누어</p>
<p>check = -1 일때, check = 0일 때, 0 &lt; check &lt; size 일 때, check = size일 때 buffer의 값이 어떻게 변화했는지를 직접 확인한 후 각각의 경우의 의미와 그때 getnextline 이 반환해야하는 이번 줄 문자열 (char *line)은 어떻게 처리해야하는지를 적었다. 그리고 다시 정리한 페이지를 pseudo code로 보고 과제를 수월하게 시작할 수 있었다.</p>
<h2 id="heading-2static-variable">2.Static variable</h2>
<p>getnextline에서 read를 호출할 때는 지금 읽는 size 바이트내에 newline이 있을지 없을지 모르는 상황이다. 호출한 후에는 file descriptor의 offset이 증가하여 만약 size 바이트의 중간에 newline이 있었다면 newline 이후의 문자들을 저장해야 다음 getnextline때 정확한 한 줄을 출력해줄 수 있다. 이를 해결하기 위해 subject에서 언급된 static varaible을 사용하였다.</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">func1</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span>
</span>{
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> var;
    var++;
    <span class="hljs-keyword">return</span> (var); 
}
</code></pre>
<p>위와 같이 static 타입으로 선언된 var은 언제나 초기화가 된다. 위의 int형의 경우 0 값을 가지게 된다. runtime에서 최초로 func1이 호출될 때, func1은 var이 0으로 초기화, 1로 증가, 1반환을 하게 된다.</p>
<p>일반적인 (auto) 변수들과 다르게 static 변수는 이후 다시 func1이 호출되었을 때 0으로 초기화되지 않고 이전 func1이 호출되어 반환했을 때의 값을 유지한다. 위의 예시에서 func1 첫 호출에서 var 값이 1인 상태로 종료되었기에 func1이 다시 호출된다면 var이 2로 증가, 2반환되는 결과를 얻게 된다.</p>
<h2 id="heading-3-bonus-problem-amp-solution">3. Bonus problem &amp; solution</h2>
<p>위의 방법으로 여러 fd를 처리하는 bonus까지 작성했으나 기계채점에서 bonus part KO가 나왔다.</p>
<p>첫번째 생각은 mandatory에서 사용한 static variable과 bonus에서 사용한 static variable의 이름이 달라 이를 static variable 2개로 기계가 인식했는가? 하는 의심이었다. 바로 bonus part 코드를 복붙하여 (이전에는 평가시 수월한 설명을 위해 간단한 버전인 mandatory를 남겼었다) 다시 제출했지만 바로 다시 KO를 받았다.</p>
<p>이후 여러 카뎃들과 같이 이야기하면서 추측한 문제점들은</p>
<ul>
<li><p>잘못된 fd를 바로 종료하지 않는다 - 내가 작성한 함수는 음수의 fd값이 들어와도 read에 집어넣고 read 결과에 따라 유효하지 않은 fd임을 확인하고 종료되는 형태였는데, 이 과정에서 메모리할당등의 작업이 발생하는 것이 문제일 수 있어 조건문으로 바로 반환되도록 수정했다.</p>
</li>
<li><p>null-termination - read 결과인 buffer는 언제나 null로 종료되지 않는데, 굳이 한칸을 더 써서 '\0' 문자를 입력하지 않고 검사하여 처리하는 방법을 사용했었다. 기계채점에서 사용한 메모리를 검사하는 과정에서 null-terminated 형태가 아니어서 문제가 되었나라고 생각하여 사이즈를 1 증가하여 '\0'으로 감싸주었다.</p>
</li>
<li><p>EOF 판단 - 이전에는 작성한 testfile이나 stdin으로 read 결과값을 조사하고 EOF을 판단했었는데, 뒤의 과제인 push swap bonus를 하는 과정에서 pipe로 fd를 넘겨주는 경우 EOF를 판단하는 조건을 다르게 해야함을 발견했다.</p>
</li>
</ul>
<p>위의 문제점들을 모두 수정한 결과 bonus part도 기계에서 통과할 수 있었다. 평가 시간의 소모를 줄이기 위해 위의 3가지를 모두 수정해서 정확히 셋 중 어느 것이 문제인지를 알 수 없다. 아마 1 또는 3번이 문제라 생각하고 fd를 추가하는 파트이기에 1번일 수도 함수의 중간과정을 생략한 입력, 출력만 보면 세번째 수정사항만이 변경되기에 3번이 기계에서 걸린 문제점일 수 있다. paco tester는 모두 통과했는데 bonus 기계채점에서 문제가 발생한다면 위의 세가지를 점검하는 것을 추천한다.</p>
]]></content:encoded></item><item><title><![CDATA[C Printf 만들기]]></title><description><![CDATA[42서울 1써클의 첫 과제로 결정한 printf는, 말그대로 c의 표준출력함수인 printf를 write, malloc, free만을 사용하여 새로 작성하는 것이 목표다. 먼저 이미 존재하는 함수의 기능을 구현하는 것이라 앞서 했던 과제에서처럼 입출력 테스트를 통해 주요 입출력을 확인하는 것으로 시작했다.
1.접근 방법
가장 먼저 떠오르는 방법은 printf의 format string 부분을 순회하며 조건문과 write 함수를 통해 출력과 반환...]]></description><link>https://joonho.hashnode.dev/c-printf</link><guid isPermaLink="true">https://joonho.hashnode.dev/c-printf</guid><category><![CDATA[beginner]]></category><category><![CDATA[42Seoul]]></category><category><![CDATA[C]]></category><category><![CDATA[printf]]></category><category><![CDATA[variadic_functions]]></category><dc:creator><![CDATA[Joonho Lee]]></dc:creator><pubDate>Mon, 24 Apr 2023 23:19:59 GMT</pubDate><content:encoded><![CDATA[<p>42서울 1써클의 첫 과제로 결정한 printf는, 말그대로 c의 표준출력함수인 printf를 write, malloc, free만을 사용하여 새로 작성하는 것이 목표다. 먼저 이미 존재하는 함수의 기능을 구현하는 것이라 <a target="_blank" href="https://joonho.hashnode.dev/libft">앞서 했던 과제에서처럼 입출력 테스트</a>를 통해 주요 입출력을 확인하는 것으로 시작했다.</p>
<h2 id="heading-1">1.접근 방법</h2>
<p>가장 먼저 떠오르는 방법은 printf의 format string 부분을 순회하며 조건문과 write 함수를 통해 출력과 반환값 계산을 동시에 하는 것이다. 이 방법의 장점은 mandatory part를 빠르게 끝낼 수 있고 코드가 짧아 시간을 아낄 수 있다. 단점은 bonus part의 경우 width, precision을 모두 매개변수로 넘겨주며 조건문으로 판단하면서 출력과 반환값을 동시에 처리하는 것이 불편하다는 점이 있다.</p>
<p>두번째로 생각한 방법은 printf 함수란 결국 format string에서 출력하는 string으로의 일대일 대응이기에 출력할 string을 malloc을 통해 작성한 후 마지막 반환값은 strlen으로 계산해주는 방법이다. 이 방법의 장점은 내부함수들에서 출력값이 char* str으로 통일성이 있고 bonus part도 한결 수월하게 작성할 수 있다 생각했다. 단점으로는 malloc 사용에 따른 할당이 되었는지 체크, 중간중간 free하는 것등을 포함하게 되어 코드가 상대적으로 복잡하다.</p>
<p>마지막으로 떠오른 방법은 연결리스트를 활용하는 방법이다. 위의 두번째 방법을 구현할 때 strjoin으로 앞에서부터 출력 문자열을 완성하고 다시 다음 문자열을 뒤에 붙여넣는 과정에서 결국 이전 출력 문자열을 반복적으로 읽고 복사하는 불필요한 과정이 반복된다. 이러한 동적할당된 배열 활용의 한계점을 처리하기 위해 literal에 대응하는 문자열을 저장한 node, %i %p 등의 태그에 따라 출력한 문자열을 저장한 node를 순서대로 연결리스트로 저장하고 마지막에는 리스트를 순회하며 프린트하는 방법이다. 장점으로는 bonus part확장이 가장 용이할 것이고 두번째 방법에 비해 불필요한 연산이 줄어든다. 단점으로는 세가지 방법중 가장 코드가 상대적으로 복잡하다.</p>
<p>위의 세가지 방법을 놓고 고민하다가 연결 리스트를 연습할 겸 세번째 방법으로 과제를 시작했다.</p>
<h2 id="heading-2variadic-function">2.variadic function</h2>
<p>printf는 매개변수의 개수가 고정되지 않기 때문에 이를 처리하기 위해 variadic function을 찾아 공부하고 구현했다.</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">int</span>    <span class="hljs-title">ft_printf</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span> *str, ...)</span>
</span>{
    va_list    va_ptr;

    va_start(va_ptr, str);
    ft_chr_to_field((<span class="hljs-keyword">char</span>) va_arg(va_ptr, <span class="hljs-keyword">int</span>))
    va_end(va_ptr);
}
</code></pre>
<p>위와 같이 고정적인 매개변수 뒤에 가변적으로 입력될 매개변수를 ... 으로 선언하고 이를 va_list로 선언하여 초기화하고 필요할 때마다 va_arg를 통해 뒤의 가변적으로 입력된 매개변수를 하나씩 지정된 형태로 꺼내어 사용할 수 있다. (운영체계에 따라 작동방식의 다소 차이가 있을 수 있지만) va_list에는 입력된 매개변수의 값을 배열처럼 연속된 메모리에 저장하고 va_arg가 호출될 때마다 지정된 type size만큼 해당 값을 반환해준다. 이때 va_ptr의 주소값이 type size씩 이동하는 경우가 있고 클러스터 mac환경에서는 va_ptr의 주소값은 고정되지만 내부에서 포인터를 움직여가며 값을 반환해주는데 이는 운영체제나 개발환경에 따라 발생하는 차이다.</p>
<h2 id="heading-3problem-amp-solving">3.problem &amp; solving</h2>
<p>연결리스트로 printf를 접근하면서 아래와 같은 구조를 사용하면서 문제를 발견하게 되었다.</p>
<pre><code class="lang-c"><span class="hljs-keyword">typedef</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">s_field</span>
{</span>
    <span class="hljs-keyword">char</span>              *str;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">s_field</span>    *<span class="hljs-title">next</span>;</span>
}                      t_field;
</code></pre>
<p>%c에 \0문자가 입력된 경우 위의 출력할 str에 "\0\0"를 저장하고 후에 '\0'을 출력해야 하는데 빈문자열인 "\0"와 구분할 수 없었다. 그렇다고 무조건 다음 값에 접근하여 읽다보면 후자의 경우에 segmentation fault가 발생한다. c에서 null-terminated string을 사용하기 때문에 발생하는 문제였다. 고민끝에 위의 구조체에 출력할 문자열의 길이를 같이 저장하는 방법을 사용하여 %c에 \0문자가 입력된 "\0\0"은 len=1, %s에 ""가 입력된 "\0"는 len=0을 지정해주어 segmentation fault를 방지하며 위의 문제를 처리할 수 있었다.</p>
<p>문제 외적인 42seoul의 과정 상에서의 문제점으로 빠른시간내에 printf를 작성해야하기 때문에 결국 첫번째 방법을 사용하여 printf를 또다시 작성했다. 어떻게 보면 시간을 낭비한 것처럼 보이지만 이때 연습한 연결리스트 덕분에 뒤의 과제들에서 수월하게 리스트와 구조체를 사용할 수 있었다.</p>
]]></content:encoded></item><item><title><![CDATA[C 함수들을 재구현해서 새로운 라이브러리 만들기]]></title><description><![CDATA[42 서울의 첫 과제인 Libft에서는 C 함수들을 재구현하고 새로운 라이브러리를 생성했다. Standard C library에 있는 함수들을 단순히 기억하고 가져다가 사용하는 것이 아니라 그 함수의 기능을 직접 테스트하고 그 입력값과 반환값이 일치하는 함수를 작성하니 각 함수를 더욱 정확하게 기억하게 되어 더 복잡한 과제를 할 때 필요한 함수를 더 쉽게 떠올리고 또한 필요하다면 함수를 작성하여 소스를 관리하는 방법을 익힐 수 있었다.
1.입력...]]></description><link>https://joonho.hashnode.dev/libft</link><guid isPermaLink="true">https://joonho.hashnode.dev/libft</guid><category><![CDATA[42Seoul]]></category><category><![CDATA[Makefile]]></category><category><![CDATA[beginner]]></category><category><![CDATA[libft]]></category><dc:creator><![CDATA[Joonho Lee]]></dc:creator><pubDate>Fri, 07 Apr 2023 07:53:09 GMT</pubDate><content:encoded><![CDATA[<p>42 서울의 첫 과제인 Libft에서는 C 함수들을 재구현하고 새로운 라이브러리를 생성했다. Standard C library에 있는 함수들을 단순히 기억하고 가져다가 사용하는 것이 아니라 그 함수의 기능을 직접 테스트하고 그 입력값과 반환값이 일치하는 함수를 작성하니 각 함수를 더욱 정확하게 기억하게 되어 더 복잡한 과제를 할 때 필요한 함수를 더 쉽게 떠올리고 또한 필요하다면 함수를 작성하여 소스를 관리하는 방법을 익힐 수 있었다.</p>
<h2 id="heading-1">1.입력, 출력 테스트와 구현방법</h2>
<p>이 과제 단계에서 해야하는 함수들은 복잡한 것이 아닌 기본적인 함수들이지만 이를 단순히 구글해서 함수의 입출력을 찾아서 구현하는 것은 과제 의도가 아니라고 판단했다. 우리가 모르는 어떤 입력값과 출력값을 가진 함수를 직접 테스트하여 그 규칙을 찾아내고 이를 구현하는 방법을 익히는 것이 주요 목표로 생각했고 다음과 같이 테스트코드, 그리고 작성, standard 라이브러리 함수와 비교 대조하며 작성했다.</p>
<pre><code class="lang-c">    <span class="hljs-keyword">char</span> *str1_atoi = <span class="hljs-string">"       -2147483649"</span>;
    <span class="hljs-keyword">char</span> *str2_atoi = <span class="hljs-string">"       -9223372036854775840"</span>;
    <span class="hljs-keyword">int</span>    result_atoi;

    result_atoi = atoi(str1_atoi);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"std atoi(%s) is %d\n"</span>, str1_atoi, result_atoi);
    result_atoi = ft_atoi(str1_atoi);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">" ft atoi(%s) is %d\n"</span>, str1_atoi, result_atoi);
    result_atoi = atoi(str2_atoi);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"std atoi(%s) is %d\n"</span>, str2_atoi, result_atoi);
    result_atoi = ft_atoi(str2_atoi);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">" ft atoi(%s) is %d\n"</span>, str2_atoi, result_atoi);
</code></pre>
<p>위와 같은 테스트를 문제가 될만한 입력값들에서 체크하며 출력하면 아래와 같이 출력되어 정확히 어떤 입력값에서 원함수와 다른 출력값이 발생하는 지를 확인하며 바로바로 코드를 수정할 수 있었다.</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-built_in">std</span> <span class="hljs-title">atoi</span><span class="hljs-params">(        <span class="hljs-number">-2147483649</span>)</span> is 2147483647
 ft <span class="hljs-title">atoi</span><span class="hljs-params">(        <span class="hljs-number">-2147483649</span>)</span> is 2147483647
<span class="hljs-built_in">std</span> <span class="hljs-title">atoi</span><span class="hljs-params">(        <span class="hljs-number">-9223372036854775840</span>)</span> is 0
 ft <span class="hljs-title">atoi</span><span class="hljs-params">(        <span class="hljs-number">-9223372036854775840</span>)</span> is 0</span>
</code></pre>
<p>자세한 함수들이나 과제는 42서울 정책으로 생략하지만 피신 때의 함수를 거의 그대로 활용할 수 있는 경우가 많았고 memmory 1byte단위, str처리, 연결리스트의 기본 조작과 관련한 개념들을 복습할 수 있었다.</p>
<p>또한 다른 카뎃분들이 작성해놓은 테스트 프로그램인 paco를 한개의 파트가 끝날 때마다 적용하여 내가 생각하지 못 했던 예외케이스도 함께 처리할 수 있었다.</p>
<h2 id="heading-2makefile-relink">2.Makefile relink</h2>
<p>피신 때의 c09와 달리 이번 libft에서는 두 개의 명령어가 같은 파일을 생성하게 되어 relink가 문제가 되었다. 조금 더 자세하게 Makefile과 relink를 설명하고 그 해결방안을 같이 이야기하겠다.</p>
<pre><code class="lang-makefile"><span class="hljs-section">%.o: %.c</span>
    <span class="hljs-variable">$(CC)</span> <span class="hljs-variable">$(CFLAGS)</span> -c <span class="hljs-variable">$&lt;</span> -o <span class="hljs-variable">$@</span>

<span class="hljs-variable">$(TARGET)</span> : <span class="hljs-variable">$(OBJECTS)</span>
            <span class="hljs-variable">$(AR)</span> rcs <span class="hljs-variable">$(TARGET)</span> <span class="hljs-variable">$^</span>
</code></pre>
<p>위와 같은 Makefile을 사용하는 것과 유사한 shell script를 사용하는 것 모두 동일한 명령어를 사용하여 결과물, TARGET이라는 파일이 생성된다. Shell script는 다시 한번 실행하는 경우 언제나 위의 gcc로 다시 object file을 생성하고 target을 생성한다. 하지만 Makefile을 다시 실행하는 경우 :로 구분된 target과 dependency 파일의 수정시간을 확인하여 object file의 수정시간이 src파일의 수정시간보다 최신인 경우 존재하는 object file이 src 파일로 생성되었다고 가정하고 다시 컴파일하지 않는다. 반대로 source file의 수정시간이 object file의 수정시간보다 최신인 경우는 object file이 생성된 이후 수정되었다고 판단하여 object file을 다시 생성한다. 규모가 작은 소스파일의 경우 큰 차이가 없지만 규모가 큰 프로젝트의 경우 새로 업데이트된 파일들만 다시 컴파일하는 makefile이 유용하게 사용할 수 있다.</p>
<p>하지만 여러가지 명령어를 직접 정의하는 경우, 특히 타겟파일을 :로 의존관계를 명시하지 않고 명령어:의존관계 파일 형태로 작성하여 명령어로 타겟파일이 생성되도록 하는 경우, 의존관계가 있는 파일이 수정되지 않아 타겟파일이 최신임에도 다시 명령어가 실행되는 경우가 있는데 이를 relink라고 한다. 이를 방지하기 위해 가능한한 직접 타겟파일의 의존관계를 문법에 맞게 명시하거나, 생성되는 시간을 기록할 수 있는 더미파일:의존파일들, 명령어:더미파일 방식으로 설정하여 리링크를 방지할 수 있다.</p>
]]></content:encoded></item></channel></rss>