<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seri_ous.log</title>
        <link>https://velog.io/</link>
        <description>https://2unbini.github.io 로 오세용</description>
        <lastBuildDate>Thu, 26 Aug 2021 05:40:34 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seri_ous.log</title>
            <url>https://images.velog.io/images/seri_ous/profile/4dff6825-aa15-446c-ae82-7d51f49c38a6/jason-hafso-UwEIvAwRg1A-unsplash.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seri_ous.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seri_ous" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Github | SSH 키 등록하기]]></title>
            <link>https://velog.io/@seri_ous/Github-SSH-%ED%82%A4-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@seri_ous/Github-SSH-%ED%82%A4-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 26 Aug 2021 05:40:34 GMT</pubDate>
            <description><![CDATA[<h2 id="github-에서-원격으로-clone-push-하기">Github 에서 원격으로 Clone, Push 하기</h2>
<p><img src="https://images.velog.io/images/seri_ous/post/82f9484b-13f8-42a6-94ee-911843b4bfcb/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-26%20%EC%98%A4%ED%9B%84%201.44.38.png" alt="Drag and Drop"></p>
<p>&#39;프로그래머는 깃허브를 쓴댄다&#39; 라는 말만 듣고 무작정 깃허브 계정을 만들고 드래그 앤 드롭으로 레포 관리를 해왔던 나는, 매번 새로운 내용을 업데이트 할 때마다 폴더를 열고, 드래그 앤 드롭을 해서, 모든 파일들이 다 업로드되길 기다려야 했다. 42서울에서 과제를 제출할 때 처음으로 <code>git clone</code> , <code>git push</code> 명령어를 쓰게 되었는데, 이를 위해서는 내가 사용하는 로컬 환경에서 SSH 키를 받아 42서울 서버에 저장해야 했다.</p>
<p>무지성으로 따라한 뒤로 의식 없이 쓰다가, 코로나 4단계로 집에 감금(?)되면서 내 맥북에서 작업하던 과제나 프로젝트 파일들을 Github에 정리해야 하는 상황이 발생했다. 귀찮아서 그냥 또다시 깃허브에서 레포 만들고 드래그 앤 드롭으로 올리다가 폴더가 꼬이는 바람에... 이번엔 유지성 유의식으로 해보자 하여 글을 정리하게 되었다.</p>
<blockquote>
<p>이 글은 <strong>MacOS</strong> 를 기준으로 작성되었습니다.</p>
</blockquote>
<br/>

<h2 id="ssh">SSH</h2>
<p>SSH는 <a href="https://ko.wikipedia.org/wiki/%EC%8B%9C%ED%81%90%EC%96%B4_%EC%85%B8">Secure Shell</a>로, 네트워크 상의 다른 컴퓨터에 접속하기 위해 사용되는 인터넷 프로토콜이다. Github 가 SSH 프로토콜을 지원하기 때문에 우리는 SSH 키를 통해 원격으로 Git Clone, Push와 같은 코드 관리를 안전하게 수행할 수 있는 것이다.
<br/></p>
<h2 id="공개키-인증-방식">공개키 인증 방식</h2>
<p>SSH는 다양한 인증 방법을 제공하는데, 그 중 우리는 공개키 인증 방식을 사용하게 된다.</p>
<p><a href="https://ko.wikipedia.org/wiki/%EA%B3%B5%EA%B0%9C_%ED%82%A4_%EC%95%94%ED%98%B8_%EB%B0%A9%EC%8B%9D">공개키 인증 방식</a>을 사용하려면 공개키와 개인키로 이루어진 키 쌍을 발급받아야 한다. 이렇게 발급받은 키는 각각 접속하고자 하는 서버에 올리고, 접속할 때 사용하게 된다.</p>
<p>공개키는 말 그대로 공개되어도 무방하지만, 개인키는 비밀번호의 역할을 하기 때문에 외부에 보여져선 안 된다. 공개키를 Github 서버에 등록해놓은 상태에서 개인키가 외부에 유출되면, 개인키를 가진 모두가 내 Git 서버에 접근할 수 있기 때문이다.</p>
<p>간단하게 SSH와 키에 대해 알아봤으면, 이제 내 로컬 환경에서 키를 발급받고, 이를 Github 서버에 올리면 된다. 이 과정에 대해서는 Github에서 상세하게 설명 해 주고 있다.</p>
<blockquote>
<p><a href="https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Generating a new SSH key and adding it to the ssh-agent</a></p>
</blockquote>
<br/>

<h2 id="ssh-키-확인하기">SSH 키 확인하기</h2>
<p>로컬 환경에서 SSH 키를 발급받기 전에, 내가 이미 발급받은 키가 있는지 확인하는 것이 좋다.</p>
<p>터미널을 켠 다음, </p>
<pre><code>ls -al ~/.ssh</code></pre><p>명령어를 입력한다.</p>
<p>디렉토리나 파일이 없습니다 라는 메시지가 뜨면 없는거고, 리스트가 나오면 이미 있는 것이다.</p>
<p>만약 있으면, 아래와 같은 형식의 공개키가 뜰 것이고, 원하는 키 쌍을 사용하면 된다.</p>
<ul>
<li>id_rsa.pub</li>
<li>id_ecdsa.pub</li>
<li>id_ed25519.pub</li>
</ul>
<p>없다면, 다음의 과정을 밟아 키를 만들어주면 된다.
<br/></p>
<h2 id="ssh-키-생성하기">SSH 키 생성하기</h2>
<p>다시 터미널을 켠 다음, </p>
<pre><code>ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;</code></pre><p>을 입력하되, &quot;&quot; 안의 이메일 형식을 내 Github 이메일 주소로 바꿔주면 된다.</p>
<p>만약, Ed25519 알고리즘을 지원하지 않는 시스템이라면 아래 명령어를 사용하면 된다.</p>
<pre><code>ssh-keygen -t rsa -b 4096 -C &quot;your_email@example.com&quot;</code></pre><p>마찬가지로 &quot;&quot; 안의 이메일은 내 Github 이메일 주소로 채워넣는다.</p>
<p>이 명령어를 통해 새로운 SSH 키 쌍이 만들어진다. 정상적으로 만들어졌다면,</p>
<pre><code>Generating public/private ed25519 key pair.
Enter a file in which to save the key (/Users/you/.ssh/id_ed25519): [Press enter]</code></pre><p>와 같은 메시지가 뜰 것이다.</p>
<p>두 번째 문장은 키를 저장할 위치를 지정해주는 것인데, 그냥 엔터를 누르면 괄호 안에 쓰여진 기본 위치에 저장된다.</p>
<p>저장 위치까지 설정됐으면, password 와 유사한 passphrase 를 입력하라는 문구가 뜬다. 암호의 역할을 하는 문장인 듯 싶다.</p>
<pre><code>&gt; Enter passphrase (empty for no passphrase): [Type a passphrase]
&gt; Enter same passphrase again: [Type passphrase again]</code></pre><p>안 해 줘도 상관 없지만, 나는 그냥 해 줬다.</p>
<p>여기까지 성공적으로 마치면, 공개키와 개인키가 저장된 위치와 fingerpring, 그리고 randomart image가 뜬다.
<br/></p>
<h2 id="ssh-키-확인하기-1">SSH 키 확인하기</h2>
<p>생성된 키를 확인하고자 한다면, <code>ls</code> 혹은 <code>cat</code> 명령어를 사용하면 된다.</p>
<pre><code>// ssh 키들의 리스트 출력
ls -al ~/.ssh

// 특정 ssh 키 출력
cat ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub</code></pre><p>위에서 설정했던(혹은 기본으로 설정된) 키 저장 위치를 적어 넣으면 확인이 가능하다.</p>
<p>Github 서버에 등록해야 할 것은 <strong>공개키</strong> 이므로, <code>cat id_ed25519.pub</code> 을 해서 나온 내용을 복사해 붙여넣으면 된다.</p>
<p>여태껏 <code>cat</code> 해서 그냥 드래그 하고 복사 붙여넣기를 했는데, 새로운 명령어를 알아냈다. <a href="https://www.lainyzine.com/ko/article/creating-ssh-key-for-github/">[참고 블로그]</a></p>
<pre><code>pbcopy &lt; ~/.ssh/id_ed25519.pub</code></pre><p>을 하면, <code>cat</code> 을 통해 나온 내용이 그대로 복사된다.</p>
<br/>


<h2 id="ssh-키-ssh-agent-에-등록하기">SSH 키 ssh-agent 에 등록하기</h2>
<p>ssh-agent 에 등록하기에 앞서, SSH 키가 존재하는지를 먼저 확인한 뒤, 등록을 진행한다.</p>
<p>ssh-agent 는 왜 쓰는가..! 를 찾아봤는데, 앞서 지정한 passphrase 를 기억해서 최초 한 번만 비밀번호를 입력하면 그 다음부턴 쭉 알아서 인증을 해 주는 기능이라고 한다. 이 말은, passphrase 를 설정하지 않은 사람이거나, SSH 키 비밀번호를 내가 직접 입력하고 싶은 사람이라면 이 단계를 굳이 하지 않아도 된다는 것.</p>
<p>SSH 키를 등록하기 위해선, 기본적으로 제공하는 <code>ssh-add</code> 명령어를 쓰거나, 이 명령어를 찾을 수 없다고 하면 macports, homebrew 혹은 외부 소스를 통해 설치해 사용하면 된다.</p>
<p>먼저, ssh-agent 를 백그라운드에서 실행한다.</p>
<pre><code>eval &quot;$(ssh-agent -s)&quot;</code></pre><p>환경에 따라서 위의 명령어가 아닌 다른 형태의 명령어가 필요할 수도 있다.</p>
<ul>
<li>root 접근 : ssh-agent 시작 전 <code>sudo -s -H</code> 입력</li>
<li><code>exec ssh-agent bash</code> 혹은 <code>exec ssh-agent zsh</code> 을 통해 ssh-agent 실행</li>
</ul>
<br/>

<p>다음으로, <code>~/.ssh/config</code> 를 수정해야 한다.</p>
<p>이 파일은 있을 수도 있고, 없을 수도 있는데, 이를 확인하려면 앞서 계속 사용한 <code>ls</code> 혹은 <code>cat</code> 명령어를 사용하면 된다. 해당 파일을 수정함으로써 ssh-agent 가 passphrase 를 내 키체인에 저장하고 자동으로 불러올 수 있게 한다.</p>
<pre><code>// 파일이 없을 때
touch ~/.ssh/config

// 파일이 있을 때 open vi vim 아무거나...
open ~/.ssh/config
vi ~/.ssh/config</code></pre><br/>

<p><code>config</code> 파일에 아래와 같은 내용을 넣어준다.</p>
<pre><code>Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ~/.ssh/id_ed25519</code></pre><p>내가 발급받은 개인키의 위치는 앞서 내가 설정한대로 (혹은 기본값으로) <code>IdentityFile</code> 의 내용을 수정해주면 된다.</p>
<p>만약, 내가 해당 키를 Github 에서만 쓰고 싶고, ssh-agent 를 쓸 필요가 없다면 아래와 같이 수정해주면 된다.</p>
<pre><code>Host github.com
    IdentityFile ~/.ssh/id_ed25519
    User git</code></pre><br/>

<p>마지막으로, ssh-agent 가 내 passphrase 를 키체인에 저장할 수 있게 한다.</p>
<pre><code>ssh-add -K ~/.ssh/id_ed25519</code></pre><p>경로와 키 이름을 발급받은 상태로 바꿔서 입력해주면 끝이다.
<br/></p>
<h2 id="github-서버에-공개키-저장하기">Github 서버에 공개키 저장하기</h2>
<p>Github 에 로그인한 뒤, Setting &gt; SSH and GPG keys 항목에 들어가면 SSH 키를 입력할 수 있다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/9da686a0-26db-4edc-9712-e62e75e701e6/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-26%20%EC%98%A4%ED%9B%84%202.29.05.png" alt=""></p>
<p>아까 <code>pbcopy</code> 명령어로 복사했던 <strong>공개키</strong>를 아래의 Key 란에 붙여넣기 해 준다.</p>
<p>제목은 그냥 내가 알아보기 쉽게 적어주면 된다. 나는 <code>Seri SSH key</code> 뭐 이렇게 적었다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/88ebb997-5290-4961-be92-838be26107d0/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-26%20%EC%98%A4%ED%9B%84%202.28.01.png" alt=""></p>
<p>이렇게 Key를 입력하고 추가하면, SSH 키가 정상적으로 등록된다.
<br/></p>
<h2 id="drag-and-drop-안녕">Drag and Drop 안녕...</h2>
<p><img src="https://images.velog.io/images/seri_ous/post/3a05bdc7-bfa4-4c90-bd0f-d1f351d32470/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-26%20%EC%98%A4%ED%9B%84%202.33.52.png" alt=""></p>
<p>더 이상 드래그 앤 드롭 말고 <code>git clone</code> 하세요<del>...</del>!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[M1 | React Native Android Studio, 시뮬레이터]]></title>
            <link>https://velog.io/@seri_ous/M1-React-Native-Android-Studio-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@seri_ous/M1-React-Native-Android-Studio-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Wed, 04 Aug 2021 05:56:41 GMT</pubDate>
            <description><![CDATA[<p>iOS 시뮬레이터까지는 <a href="https://velog.io/@seri_ous/M1-React-Native-%EC%84%A4%EC%B9%98-iOS-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%A4%ED%96%89">했다</a>.
하지만 iOS 앱만 확인할 순 없잖아..! 안드로이드도 잇따구..!
해서 설치 시작.</p>
<blockquote>
<p><a href="https://medium.com/@davidjasonharding/developing-a-react-native-app-on-an-m1-mac-without-rosetta-29fcc7314d70">Developing a React Native app on an M1 Mac without Rosetta</a></p>
</blockquote>
<p>사실 어제 실행해본 run-ios가 로제타 없이 돌아간 것 같아서 이게 어떻게 됐지..? 하는 마음에 서치하다가</p>
<p>안드로이드 스튜디오 깔고 하는 방법이 잘 정리돼있는 위 블로그를 참조했다.</p>
<br/>

<h2 id="안드로이드-스튜디오">안드로이드 스튜디오</h2>
<h3 id="안드로이드-스튜디오-설치">안드로이드 스튜디오 설치</h3>
<blockquote>
<p><a href="https://developer.android.com/studio/preview">Android Studio install</a></p>
</blockquote>
<p>안드로이드 스튜디오를 설치해준다.</p>
<p>블로그에서 &#39;Arctic Fox&#39;를 설치하라고 돼있었는데 사이트에는 범블비라고 적혀 있었다. 이전 버전인가 싶어서 찾아봤는데, 명칭이 다른 것 같다. 그냥 설치 해 줌.</p>
<p>설치 과정에선 딱히 할 게 없다.
<br/></p>
<h3 id="avd-설치">AVD 설치</h3>
<p>안드로이드 스튜디오를 잘 깔았으면 들어가준다.</p>
<p>메인 화면이 이렇게 뜨는데, 여기서 하단의 <code>More Actions</code> 를 클릭해 <code>ADV Manager</code> 로 들어가준다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/f8d8e04d-1d25-436e-934a-b69b997ff6f8/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%202.42.50.png" alt=""></p>
<p>그럼 이런 창이 나오는데, 밑에 <code>Create Virtual Device</code> 를 눌러 새 가상 기기를 만들어주면 된다.
<img src="https://images.velog.io/images/seri_ous/post/a45a2eff-1408-44c8-9f7d-a12dd6f64f2d/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%202.43.07.png" alt=""></p>
<p>기기 설정은 내가 원하는대로 맞춰주고, Finish를 눌러주면 아래와 같이 기기가 생성된다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/d062e1f8-c2cc-47c3-a6ca-d049d3614b7b/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%201.06.56.png" alt=""></p>
<p><img src="https://images.velog.io/images/seri_ous/post/dfed1104-1cc0-42b1-8b1c-41f9f6a0910d/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%201.07.02.png" alt=""></p>
<p><img src="https://images.velog.io/images/seri_ous/post/3335e0ff-2d13-41ca-9a6c-5bf904783a65/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%201.07.08.png" alt=""></p>
<p><img src="https://images.velog.io/images/seri_ous/post/d6a9ea28-1620-4623-a181-aa9c72c47ebb/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%201.07.19.png" alt=""></p>
<br/>


<h3 id="android-simulator-실행">Android Simulator 실행</h3>
<p>시뮬레이터를 실행하기 전에, 프로젝트와 셸의 환경을 설정해줘야 한다.</p>
<p>프로젝트의 <code>android</code> 디렉토리로 간 뒤, <code>[local.properties](http://local.properties)</code> 라는 파일을 하나 만들어준다.</p>
<p>eunbin 대신 본인 username을 쓰면 된다.</p>
<pre><code>sdk.dir = /Users/eunbin/Library/Android/sdk</code></pre><p>다음으로 <code>~/.zshrc</code> 를 열어 <code>ANDROID_SDK</code> 경로를 추가해준다.
역시 eunbin 자리에 username 쓰기!</p>
<pre><code>export ANDROID_SDK=/Users/eunbin/Library/Android/sdk</code></pre><p>그리고 android 디렉토리에 있는 <code>[gradle.properties](http://gradle.properties)</code> 파일을 열어 Flipper 버전을 <code>0.91.1</code> 로 수정해준다. 코드는 0.75.1 버전으로 쓰여져 있기 때문에 숫자만 살짝 바꿔주면 된다.</p>
<pre><code>FLIPPER_VERSION=0.91.1</code></pre><p>그리고 <code>./gradlew clean</code> 을 해 준다.</p>
<blockquote>
<p>여기까지 하고 잘 되는 분들은 <strong>다시 Android Simulator 실행</strong> 으로 건너뛰세용.</p>
</blockquote>
<br/>

<p>나는 여기서 문..제..?가 생겼는데 <code>./gradlew clean</code> 을 하라고 해서 했는데</p>
<blockquote>
<p>This operation couldnt be completed. Unable to locate a Java Runtime.</p>
</blockquote>
<p>이딴 에러가 떴다.</p>
<p>역시 갓글링을 해보았더니 <strong>java가 없어서</strong> 그런거란다.</p>
<p>M1으로 바꾸면서 아무거나 막 다운받는다고 다 되는 게 아니란 걸 깨닫곤, java의 어떤 버전이 M1에서 유효한지를 검색해봤다.</p>
<p>Oracle에서 제공하는 버전은 아직 Apple Silicon을 지원하지 않는다고 한다. 그래서 찾아낸 다른 방법! <strong>AZUL에서 다운</strong>받으면 된다.</p>
<blockquote>
<p><a href="https://www.azul.com/downloads/?version=java-8-lts&amp;package=jdk">OpenJDK version 8 (다른 버전을 다운받으시려면 Azul로 고고)</a></p>
</blockquote>
<br/>

<p>근데 또 난 아무 의식 없이 OpenJDK 16 버전을 다운받았었다.</p>
<p>다운 완료하고, 환경변수까지 잘 지정해줬다고 생각하고 다시 clean 을 해 보니 이번엔 다른 문제가 발생했다.</p>
<blockquote>
<p>Could not compile settings file &#39;.../settings.gradle&#39;.</p>
</blockquote>
<p>다시 열심히 구글링을 해 보니 해당 에러에 포함된 내용 중에 <code>Unsupported class file major version 60</code> 라는 말이 <strong>Java 16이 아직 제공하지 않는</strong> 기능을 썼다는 거란다.</p>
<p><a href="https://stackoverflow.com/questions/67462224/android-studio-could-not-compile-settings-file">관련 링크 : Android Studio: Could not compile settings file</a></p>
<p>그래서 <strong>Java 8 버전</strong>으로 다시 다운받았고, Java는 맥 상에서 자동으로 최상위 버전을 기본값으로 설정해주기 때문에 Java 16은 쓰레기통으로 갖다 버렸다.</p>
<p>⇒ 버린 뒤 <strong>환경변수 재적용</strong>을 안 했더니 에러 한 번 더 남... <code>source</code> 해주세요..</p>
<p>여차저차해서 다시 clean을 해 보니, 이번엔 잘 됐다.
<br/></p>
<p>자바 환경변수 세팅은 아래 글을 참고했다. 그냥 하던 대로 <code>~/.zshrc</code> 에 넣어주고 <code>source</code> 해 주면 된다 (MacOS 11 Big Sur 기준).</p>
<p><a href="https://mkyong.com/java/how-to-set-java_home-environment-variable-on-mac-os-x/">관련 링크 : How to Set $JAVA_HOME environment variable on macOS - Mkyong.com</a></p>
<br/>

<h3 id="다시-android-simulator-실행">다시 Android Simulator 실행</h3>
<p>./gradlew clean 까지 잘 마쳤으면, run을 할 차례다.</p>
<p>하지만 run을 하기 전에 먼저 아까 만들어놨던 가상 기기를 실행시킨다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/762631ed-a09c-41d6-972b-6c2f4985dc51/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%201.07.19.png" alt=""></p>
<p>플레이 버튼처럼 생긴 저걸 누르면, 내가 세팅한 스펙의 안드로이드 가상 기기가 화면에 뜬다.</p>
<p>그럼 이제 진짜최종런을 하면 된다.</p>
<pre><code>npx react-native run-android</code></pre><p>하면..!!!</p>
<br/>

<p>... 낄낄
<img src="https://images.velog.io/images/seri_ous/post/9fd5d6ff-d3fc-48aa-b14a-d24db24443be/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%201.35.34.png" alt=""></p>
<p>또 에러가 떴다.</p>
<blockquote>
<p>Failed to launch emulator. Reason: No emulators found as an output of <code>emulator -list-avds</code>.</p>
</blockquote>
<blockquote>
<p>Failed to install the app. Please accept all necessary Android SDK licenses using Android SDK Manager: &quot;$ANDROID_HOME/tools/bin/sdkmanager --licenses&quot;.</p>
</blockquote>
<p>버전 문제라느니... 무슨 문제라느니... 라는 기타 등등 아주 많은 이야기들이 있었는데, 그 중 <strong>SDK 라이선스를 체크</strong>해줘야 한다는 것이 있었다.
<br/></p>
<p>다시 안드로이드 스튜디오를 들어가서,</p>
<blockquote>
<p>Preferences → Appearance&amp;Behavior → System Settings → Android SDK</p>
</blockquote>
<p>탭으로 들어가면, 뭐가 설치 돼있고, 안돼있는지 확인할 수 있다.</p>
<p>나는 저기에서 <code>Android SDK Command-line Tools(latest)</code> 항목이 Not Installed 로 돼있었고,</p>
<p>해당 항목을 체크한 뒤 하단의 Apply 를 해 Install 할 수 있었다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/8ef4037c-0a3a-4974-b653-589ffb94bc1d/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%202.54.21.png" alt=""></p>
<p>이렇게 체크 해 주고, 스택오버플로우 형님들의 조언에 따라 <code>~/.zshrc</code> 안에 아래 내용을 넣어 줬다. <your_computer_name> 에는 본인 맥 username을 넣어주면 된다.</p>
<pre><code>export ANDROID_SDK=/Users/&lt;your_computer_name&gt;/Library/Android/sdk
export PATH=/Users/&lt;your_computer_name&gt;/Library/Android/sdk/platform-tools:$PATH</code></pre><p><a href="https://stackoverflow.com/questions/38835931/react-native-adb-reverse-enoent">관련 링크 : React Native adb reverse ENOENT</a></p>
<br/>

<p><code>source</code> 를 잊지 않고 해 주면, 진짜 최종 끝이 난다.</p>
<p>다시 RUN 해 주면...!!</p>
<pre><code>npx react-native run-android</code></pre><p>흑흑... 드디어 실물(?) 영접..,,</p>
<p><img src="https://images.velog.io/images/seri_ous/post/097ff32b-0401-4940-ae4b-d45f819bcd1e/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%ED%9B%84%2012.48.27.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[M1 | React Native 설치, iOS 시뮬레이터 실행]]></title>
            <link>https://velog.io/@seri_ous/M1-React-Native-%EC%84%A4%EC%B9%98-iOS-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%A4%ED%96%89</link>
            <guid>https://velog.io/@seri_ous/M1-React-Native-%EC%84%A4%EC%B9%98-iOS-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%A4%ED%96%89</guid>
            <pubDate>Wed, 04 Aug 2021 05:39:17 GMT</pubDate>
            <description><![CDATA[<p>늙고 소중한(했던) 나의 13년형 맥북 프로를 보내고...
젊고 귀여운 나의 20년형 맥북 에어와 함께 리액트 네이티브를 시작해보았다.</p>
<p>왜 늙고 소중했던 13년형 맥북은 안 되냐?</p>
<p>구구절절 이야기가 있지만, 대충 시뮬레이터 돌려보고 기타등등 하려면 Big Sur 로 업데이트 해야 하는데, 업데이트를 하면 내 맥북은 벽돌이 되기 때문에 더러운 꼴 안 보고 그냥 질러버렸다.</p>
<p>그럼 젊고 귀여운 20년형 맥북은 잘 되냐? 에러의 향연 끝에 되긴 된다.</p>
<p>물론 아직 안드로이드 시뮬은 안 돌려봄 ㅋ
-&gt; 안드로이드 시뮬은 <a href="https://velog.io/@seri_ous/M1-React-Native-Android-Studio-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0">다음 글</a>에!</p>
<h2 id="리액트-네이티브-설치">리액트 네이티브 설치</h2>
<p>를 위해서는 <code>npm</code> 이 필요한데 이 <code>npm</code> 은 풀네임이 node package manager 이다.</p>
<p>이 말은 <code>node</code> 가 필요하다는 것일테고, 그럼 <code>node</code> 를 설치하려면 뭐가 필요하냐?</p>
<p><code>homebrew</code> 킹갓브루가 필요하다.</p>
<p>그래서 <strong><code>homebrew</code> → <code>node</code> → <code>react native</code></strong> 순으로 설치 해 줄건데,</p>
<p>이 과정에서 <strong>필요한 툴, 의존성 관리 도구, ide,</strong> 그리고 결과적으로 <code>M1</code> 환경에서 손쉬운 개발을 하게 해 줄 <strong><code>iTerm</code> 과 <code>Rosetta</code></strong> 를 함께 설치 할 것이다.</p>
<h3 id="xcode-설치">Xcode 설치</h3>
<p>나의 새 맥북 구매를 하게끔 한 장본인.
앱스토어 가서 그냥 다운 받으면 된다. 시간이 오래 걸리니 얘를 가장 먼저 다운로드 하는 것이 좋다.</p>
<p>Xcode 설치가 안 돼 있으면, 즉 Apple 개발자 도구에 대한 다운로드나 설정이 완료되지 않으면, 뒤의 과정에서 오류가 생길 수 있으니 반드시 설치해놓기로 하자.</p>
<br/>

<h3 id="rosetta-설치">Rosetta 설치</h3>
<ul>
<li>Rosetta 는 M1과 같이 ARM 기반의 칩을 사용하는 환경에서 x86 아키텍처를 사용하여 디자인된 Intel 기반 칩용 앱을 실행할 수 있는 lifeline 이라고 한다.</li>
</ul>
<pre><code>/usr/sbin/softwareupdate --install-rosetta --agree-to-license</code></pre><p>  → Rosetta 설치 없이 실행하려면 <a href="https://medium.com/@davidjasonharding/developing-a-react-native-app-on-an-m1-mac-without-rosetta-29fcc7314d70">이 블로그</a> 참조!
<br/></p>
<h3 id="iterm-설치">iTerm 설치</h3>
<ul>
<li>쉽게 생각하면 맥에 기본적으로 있는 <a href="http://terminal.app">terminal.app</a> 과 동일한 기능을 하는 프로그램이다.</li>
<li>터미널로 그냥 시작하지 않고 iTerm을 다운받는 이유는, 당신이 M1을 사용하기 때문이다.</li>
<li>터미널은 AppleSilicon 에서 작동할 수 있도록 남겨 두고, iTerm 을 Rosetta 로 돌릴 수 있도록 나누어 주는 것이다. 구분을 위해서 다운받는 것이니 터미널에 Rosetta를 돌리게 하고 iTerm에 AppleSilicon을 돌리게 하는 것도 당연히 된다.</li>
</ul>
<blockquote>
<p><a href="https://iterm2.com/">iTerm</a></p>
</blockquote>
<p>위 링크에 들어가서 다운받자.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/3ef34e3e-46a4-4a78-ab8b-12d30ee902dc/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%EC%A0%84%201.30.34.png" alt=""></p>
<p>설치한 뒤, Finder에서 iTerm을 응용 프로그램 디렉토리로 옮긴다.</p>
<p>command + i혹은 마우스 우클릭 + 정보 가져오기 로 iTerm 정보를 띄우고,</p>
<p>Rosetta를 사용하여 열기를 클릭해준다.
<br/></p>
<h3 id="homebrew-설치">Homebrew 설치</h3>
<ul>
<li>킹갓브루는 맥에서 다양한 툴을 설치할 수 있는 패키지 매니저다.</li>
<li>킹갓브루는 한글로 된 홈페이지도 있다.</li>
</ul>
<blockquote>
<p><a href="https://brew.sh/index_ko">Homebrew</a></p>
</blockquote>
<pre><code>/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;</code></pre><p>로 homebrew 를 설치해주고,</p>
<pre><code>brew --version</code></pre><p>으로 제대로 잘 깔렸는지 확인 해 준다.</p>
<p>homebrew를 설치했으니 이제 본격! 설치를 해 보자.</p>
<p>차례로 설치해 줄 것들은 <code>node</code>, <code>watchman</code>, <code>cocoapods</code>, <code>ffi</code> 이다.
<br/></p>
<h3 id="node-설치">node 설치</h3>
<ul>
<li>모두가 알고 있는 그 node.js 가 맞다.</li>
</ul>
<pre><code>brew install node</code></pre><p>설치해주고,</p>
<pre><code>node -v
npm -v</code></pre><p>로 확인 해 준다.</p>
<p>node가 정상적으로 설치 됐으면 npm도 잘 설치 됐을 것이다.
<br/></p>
<h3 id="watchman-설치">watchman 설치</h3>
<ul>
<li>특정 폴더나 파일을 감시해서 변경됐을 때 어떠한 동작을 하는 툴이다.</li>
<li>react native에서 코드를 수정했을 때 바로 적용할 수 있게끔 해 준다.</li>
</ul>
<pre><code>brew install watchman</code></pre><pre><code>watchman --version</code></pre><p>너무 쉽다.</p>
<p>근데 버전 확인해보면 일반적으로 우리가 아는 그 버전 형태가 아니라 날짜로 나오는 경우가 있는데, 홈브루 버그인 듯 싶다. → <a href="https://github.com/facebook/watchman/issues/915">관련 링크</a>
<br/></p>
<h3 id="cocoapods-설치">cocoapods 설치</h3>
<ul>
<li>늙은 13년형 맥북으로 이거 설치하려다가 온갖 짭새가 날아들어서 에러 파티를 일으킨 적이 있다.</li>
<li>하지만 이번엔 다르다. 새 맥북은 꼬일 일이 없다.</li>
<li>아무튼 cocoapods는 의존성 관리자로, iOS 개발을 할 때 뗄래야 뗄 수 없다고 한다.</li>
</ul>
<pre><code>sudo gem install cocoapods</code></pre><p>갑자기 gem이다.</p>
<p>하지만 그냥 해 주자. 왜냐면 맥에는 ruby가 기본으로 깔려 있기 때문에 그냥 해 주면 된다.</p>
<p>sudo 명령어를 썼기 때문에 맥북 암호를 입력해준다.</p>
<p><del><em>이 암호는 쓰여지는지 아닌지도 모르게 그냥 가려져 있기 때문에 처음 이 광경을 마주하는 사람은 굉장히 당황스러울 것이다. 프로그래밍 하나도 몰랐던 과아거의 제가 그랬음...</em></del></p>
<pre><code>pod --version</code></pre><p>확인.
<br/></p>
<h3 id="ffi-설치">ffi 설치</h3>
<ul>
<li>왜 하는 지 잘 모르겠어서 건너뛰었다가 뒤에서 에러를 맛 보고 다시 그냥 깔았다.</li>
<li>얘 때문에 에러가 나고 안나고 하는지는 솔직히 모르겠다.</li>
</ul>
<pre><code>sudo gem install ffi</code></pre><br/>

<h3 id="react-native-설치">React Native 설치</h3>
<p>대망의 그 것.</p>
<pre><code>npm install -g react-native-cil</code></pre><pre><code>react-native --version</code></pre><p>설치 완료.</p>
<br/>

<h2 id="나의-첫-react-native-project">나의 첫,, React Native Project...</h2>
<p>를 위해서는 일단 개발환경을 맞춰줘야 한다.</p>
<h3 id="xcode-설정">Xcode 설정</h3>
<p>설치 후 실행한 뒤</p>
<p>  Preference &gt; Location &gt; Command Line Tools</p>
<p>경로에 CLT 가 설정되어있는지 확인한다.</p>
<h3 id="simulator-설치">Simulator 설치</h3>
<p>위에서 봤던 Locations 바로 옆 Components에 시뮬레이터 목록이 있다.</p>
<p>원하는 버전으로 다운로드하면 된다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/27069124-7122-48dc-b42c-4b97f2720fe7/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%EC%A0%84%201.55.35.png" alt=""></p>
<br/>

<h3 id="react-native-project">React Native Project</h3>
<p>iTerm을 열어 내가 프로젝트를 저장하고자 하는 경로로 이동한다.</p>
<pre><code>// 원하는 디렉토리로 이동
cd ~/Dev/React-Native</code></pre><p>프로젝트를 실행한다.</p>
<pre><code>npx react-native init MyFirstRN</code></pre><p>npx를 빼도 되긴 하는 것 같은데, 무슨 차이인지는 나중에 알아봐야겠다.</p>
<pre><code>npx react-native start</code></pre><p>Metro 를 시작하는 명령어인데, start 를 하지 않고 바로 run-ios 로 시뮬레이터를 실행해도 자동으로 시작된다.</p>
<br/>

<p>프로젝트가 정상적으로 잘 만들어졌으면, 어플리케이션을 실행하기 전 iOS 앱 실행에 필요한 라이브러리들을 설치해준다.
앞에서 열심히 깔아제꼈던 것들 중 <strong>cocoapods</strong>를 이용하면 된다.
iOS 앱 실행을 위한 과정이기 때문에 프로젝트 내의 ios 디렉토리로 들어가서 설치한다.</p>
<pre><code>pod install</code></pre><p>cf) 뭔가 변경이 있을 땐 <code>pod update</code> 혹은 <code>pod deintegrate</code> 후 <code>pod install</code> 을 하면 적용된다.</p>
<p>다 완료되면 다시 프로젝트의 루트 경로로 돌아와서 실행 해 준다.</p>
<pre><code>npx react-native run-ios</code></pre><p><img src="https://images.velog.io/images/seri_ous/post/024fddde-a273-43c8-a106-042285c55711/%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202021-08-04%20%EC%98%A4%EC%A0%84%201.13.42.png" alt=""></p>
<p>흑흑...</p>
<p>왜 그랬는 지 모르겠지만, 이렇게 되기까지 수많은 에러와 워닝을 맛보았다.</p>
<p>관련해서 어떤 워닝과 어떤 해결책이 있었는지는 링크로 대체한다...</p>
<br/>

<p>망할에러새끼</p>
<p><a href="https://fomaios.tistory.com/entry/%ED%95%B4%EA%B2%B0%EB%B2%95-%ED%8F%AC%ED%95%A8-The-iOS-Simulator-deployment-target-IPHONESDEVELOPMENTTARGET-is-set-to-80but-the-range-of-suppoted-deployment-target-vesions-is-90-to-14499">[Error 해결법 포함] The iOS Simulator deployment target &#39;IPHONES_DEVELOPMENT_TARGET&#39; is set to 8.0,but the range of suppoted deployment target vesions is 9.0 to 14.4.99.</a></p>
<p><a href="https://success206.tistory.com/150">[React Native] iOS 실행시 에러(Flipper 관련)</a></p>
<p><a href="https://github.com/CocoaPods/CocoaPods/issues/6172">[!] Invalid <code>Podfile</code> file: [!] Specifying multiple <code>post_install</code> hooks is unsupported · Issue #6172 · CocoaPods/CocoaPods</a></p>
<p><a href="https://velog.io/@yeseul/Mac-React-Native-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%97%90%EB%9F%AC%ED%95%B4%EA%B2%B0">(Mac) React Native 개발환경 구축하기 &amp; 에러해결</a></p>
<p><a href="https://m.blog.naver.com/bb_/221920061509">[Xcode] The linked library &#39;파일명&#39; is missing one more architectures required by this target: arm64.</a></p>
<p><a href="https://yenos.tistory.com/entry/tipxcode-511-missing-required-architecture-arm64-error">[tip]xcode 5.1.1 missing required architecture arm64 error</a></p>
<p><a href="https://stackoverflow.com/questions/65364886/react-native-on-apple-silicon-m1-the-linked-library-libpods-projectname-a-is">React Native on Apple Silicon M1 - The linked library &#39;libPods-ProjectName.a&#39; is missing one or more architectures required by this target: x86_64</a></p>
<p><a href="https://khushwanttanwar.medium.com/xcode-12-compilation-errors-while-running-with-ios-14-simulators-5731c91326e9">Xcode 12 Compilation Errors (While running with iOS 14 Simulators)</a></p>
<p>설치 시 참고했던 블로그</p>
<p><a href="https://velog.io/@sseumeong2/React-Native-MACM1-%EA%B0%9C%EB%B0%9C-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95-%ED%95%98%EA%B8%B0">[React Native] MAC(M1) 개발 환경 설정 하기</a></p>
<p><a href="https://velog.io/@taese0ng/M1-%EB%A7%A5%EC%97%90%EC%84%9C-React-Native-%EC%84%B8%ED%8C%85%ED%95%98%EA%B8%B0">M1 맥에서 React Native 세팅하기</a></p>
<p>로제타 없이 고고</p>
<p><a href="https://medium.com/@davidjasonharding/developing-a-react-native-app-on-an-m1-mac-without-rosetta-29fcc7314d70">Developing a React Native app on an M1 Mac without Rosetta</a></p>
<p><a href="https://handi.dev/blog/how-run-react-native-on-macbook-m1-apple-silicon">How to Run and Build React Native on Macbook Pro M1 Apple Silicon</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift | 구조체와 클래스, 공식 문서 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%EA%B5%AC%EC%A1%B0%EC%B2%B4%EC%99%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B3%B5%EC%8B%9D-%EB%AC%B8%EC%84%9C-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%EA%B5%AC%EC%A1%B0%EC%B2%B4%EC%99%80-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B3%B5%EC%8B%9D-%EB%AC%B8%EC%84%9C-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Sun, 25 Jul 2021 07:24:14 GMT</pubDate>
            <description><![CDATA[<h1 id="구조체와-클래스">구조체와 클래스</h1>
<hr>
<p>구조체와 클래스는 일반적인 목적의, 유연한 구조를 가진 코드를 구성하는 기본 요소다. 구조체와 클래스에 기능을 추가하기 위해 상수, 변수, 함수 등을 선언하는 것과 같은 이유로 프로퍼티와 메소드를 정의한다.</p>
<p>다른 프로그래밍 언어와 달리, 스위프트는 사용자 정의 구조체와 클래스를 만들기 위해 인터페이스와 구현을 따로 나눌 필요가 없다. 스위프트에선 구조체와 클래스를 하나의 파일에 정의하고나면 그에 대한 외부 인터페이스는 자동적으로 만들어져 다른 코드에서 사용할 수 있게 된다.</p>
<blockquote>
<p>NOTE : 클래스의 인스턴스는 일반적으로 <em>객체*로 알려져 있다. 하지만, 스위프트의 구조체와 클래스는 다른 언어에 비해 더 기능에 가깝고, 이 챕터에서 클래스나 구조체 타입의 인스턴스들에 적용되는 기능에 초점을 맞춰 설명한다. 이런 이유로 *인스턴스</em> 라는 용어가 더 일반적으로 사용됐다.</p>
</blockquote>
<h2 id="구조체와-클래스-비교하기">구조체와 클래스 비교하기</h2>
<p>스위프트의 구조체와 클래스는 많은 공통점이 있다.</p>
<ul>
<li>값을 저장하기 위해 프로퍼티를 정의한다.</li>
<li>기능을 제공하기 위해 메소드를 정의한다.</li>
<li>서브스크립트 문법을 사용해 값에 접근하기 위해 서브스크립트를 정의한다.</li>
<li>초기 상태를 설정하기 위해 이니셜라이저를 정의한다.</li>
<li>기본적으로 구현된 것에 기능을 추가할 수 있다.</li>
<li>특정 종류에 기본적인 기능을 부여하기 위해 프로토콜을 따른다.</li>
</ul>
<p>더 많은 정보는 <a href="https://docs.swift.org/swift-book/LanguageGuide/Properties.html">프로퍼티</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Methods.html">메소드</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Subscripts.html">서브스크립트</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Initialization.html">초기화(이니셜라이제이션)</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Extensions.html">익스텐션</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Protocols.html">프로토콜</a>을 확인하자.</p>
<p>클래스는 구조체가 갖지 않은 더 많은 기능을 가지고 있다.</p>
<ul>
<li>상속을 통해 다른 클래스의 특성을 상속받을 수 있다.</li>
<li>타입 캐스팅을 통해 런타임에 클래스 인스턴스의 타입을 확인할 수 있다.</li>
<li>정리자(디이니셜라이저)를 사용해 클래스의 인스턴스에 대한 모든 정보(메모리)를 정리할 수 있다.</li>
<li>참조계수를 통해 클래스 인스턴스에 한 개 이상의 참조를 할 수 있다.</li>
</ul>
<p>더 많은 정보는 <a href="https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html">상속</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html">타입 캐스팅</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html">정리자(디이니셜라이제이션)</a>, <a href="https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html">자동 참조 계수(Automatic Reference Counting)</a>을 확인하자. </p>
<p>클래스가 가진 추가적인 기능은 복잡도를 증가시켜 더 많은 비용이 들게 한다. 일반적인 가이드라인 상에서 구조체를 선호하는데, 이는 구조체가 더 식별하기 쉽기 때문이며, 클래스는 정말 필요하고 클래스를 사용하기 적절한 때에 사용한다. 이는 현실에서 사용자 정의 데이터 타입을 정의할 때 구조체와 열거형을 많이 쓴다는 의미가 된다. 더 자세한 비교는 <a href="https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes">&#39;구조체와 클래스 중 뭘 선택하는가&#39;</a>를 확인하자.</p>
<blockquote>
<p>NOTE: 클래스와 액터는 행동과 특성에 있어 많은 유사성이 있다. 액터에 대한 정보는 <a href="https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html">Concurrency</a> 에서 볼 수 있다.</p>
</blockquote>
<h2 id="정의-구문문법">정의 구문(문법)</h2>
<p>구조체와 클래스는 유사한 정의 구문을 가진다. 구조체는 <code>struct</code> 키워드로, 클래스는 <code>class</code> 키워드로 시작한다. <code>{}</code> 안에 각 구조체와 클래스의 정의를 적어 넣으면 된다.</p>
<pre><code class="language-swift">struct SomeStructure {
    // 구조체 정의
}

class SomeClass {
    // 클래스 정의
}</code></pre>
<blockquote>
<p>NOTE : 구조체와 클래스와 같은 새로운 타입을 정의할 땐 <code>대문자 카멜 케이스</code> 로 이름을 정한다. 스위프트의 기본 타입들(String, Bool, Int ...)과 같이 말이다. 프로퍼티와 메소드의 이름은 <code>소문자 카멜 케이스</code> 로 지으면 된다.</p>
</blockquote>
<p>구조체와 클래스를 정의하는 예시는 다음과 같다.</p>
<pre><code class="language-swift">struct Resolution {
        var width = 0
        var height = 0
}

class VideoMode {
        var resolution = Resolution()
        var interlaced = false
        var frameRate = 0.0
        var name: String?
}</code></pre>
<p>위 예시에서 화면의 해상도를 설명하는 <code>Resolution</code> 이라는 구조체를 정의했다. 이 구조체는 너비와 높이의 두 저장 프로퍼티를 갖는다. 저장 프로퍼티란 구조체나 클래스의 부분으로 값을 저장하는 상수나 변수를 일컫는다. 이 두 프로퍼티는 초기값 <code>0</code> 을 갖는 <code>Int</code> 형으로 정의되어있다.</p>
<p>또한 비디오 모드와 디스플레이에 대한 정보를 담는 <code>VideoMode</code> 라는 클래스도 정의되어 있다. 이 클래스는 네 개의 저장 프로퍼티를 갖는다. 첫 번째 <code>resolution</code> 은 <code>Resolution</code> 타입의 인스턴스다. 다른 세 가지의 프로퍼티는 각자 Bool, Double, Optional String 타입으로 정의돼있다. 여기서 <code>name</code> 프로퍼티는 옵셔널 타입이기 때문에 자동적으로 <code>nil</code> 또는 <code>no name value</code> 로 정의된다.</p>
<h2 id="구조체와-클래스-인스턴스">구조체와 클래스 인스턴스</h2>
<p><code>Resolution</code> 구조체와 <code>VideoMode</code> 클래스는 그 자체로 특정 해상도나 비디오 모드를 설명할 수 없다. 이를 위해서 구조체 또는 클래스의 인스턴스를 만들어야 한다.</p>
<p>인스턴스를 생성하는 구문은 클래스와 구조체가 유사하다.</p>
<pre><code class="language-swift">let someResolution = Resolution()
let someVideoMode = VideoMode()</code></pre>
<p>구조체와 클래스 모두 새 인스턴스 생성에 초기화 구문이 사용된다. 위에서 <code>Resolution()</code> <code>VideoMode()</code> 와 같이 써준 것이 가장 간단한 초기화 구문이다. 이를 통해 구조체와 클래스에 정의된 내용과 그것들의 초기값이 담긴 새 인스턴스를 생성할 수 있다. 클래스와 구조체 초기화는 <a href="https://docs.swift.org/swift-book/LanguageGuide/Initialization.html">Initialization</a>에 더 자세히 쓰여 있다.</p>
<h2 id="프로퍼티에-접근하기">프로퍼티에 접근하기</h2>
<p>점을 사용해 인스턴스의 프로퍼티에 접근할 수 있다. 공백 없이 마침표(.)로 인스턴스 이름과 프로퍼티 이름을 분리한다.</p>
<pre><code class="language-swift">print(&quot;someResolution의 너비는 \(someResolution.width) 이다.&quot;)
// someResolution의 너비는 0이다.</code></pre>
<p>여기서 <code>someResolution</code>의 <code>width</code> 프로퍼티의 초기값이 0으로 설정돼있기 때문에 위와 같은 출력값이 나올 것이다.</p>
<p>서브프로퍼티로 들어갈수도 있는데, <code>VideoMode</code>의 <code>resolution</code> 프로퍼티의 <code>width</code> 프로퍼티에 접근하는 것도 가능하다는 뜻이다.</p>
<pre><code class="language-swift">print(&quot;someVideoMode의 너비는 \(someVideoMode.resolution.width) 이다.&quot;)
// someVideoMode의 너비는 0이다.&quot;</code></pre>
<p>프로퍼티 변수에 새로운 값을 할당할 때도 점을 사용한다.</p>
<pre><code class="language-swift">someVideoMode.resolution.width = 1280
print(&quot;someVideoMode의 너비는 \(someVideoMode.resolution.width) 이다.&quot;)
// someVideoMode의 너비는 1280이다.&quot;</code></pre>
<h2 id="구조체-타입의-memberwise-initializer">구조체 타입의 Memberwise Initializer</h2>
<p>모든 구조체는 새로운 구조체 인스턴스를 생성할 때 *<em><code>memberwise initializer</code> *</em> 를 사용해 자동으로 프로퍼티들을 초기화할 수 있다. 프로퍼티의 초기화 값은 이름으로 초기화할 수 있다.</p>
<pre><code class="language-swift">let vga = Resolution(width: 640, height: 480)</code></pre>
<p>구조체와 달리 클래스는 기본적으로 memberwise initializer가 없다.</p>
<p>초기화에 대한 내용은 <a href="https://docs.swift.org/swift-book/LanguageGuide/Initialization.html">Initialization</a> 을 확인하자.</p>
<div style="padding-bottom:50px"></div>

<h1 id="구조체와-열거형은-값-타입이다">구조체와 열거형은 값 타입이다</h1>
<hr>
<p>값 타입이라는 것은 변수나 상수에 할당될 때, 혹은 함수에 전달될 때 그 값이 복사된다는 것을 의미한다.</p>
<p>스위프트의 모든 기본 타입들 - 정수형, 소수형, 불리언, 문자열, 배열, 딕셔너리들 -은 모두 값 타입으로, 구조체 뒤에 그 내용을 숨기고 있을 뿐이다.</p>
<p>모든 구조체와 열거형은 스위프트에서 값 타입이다. 이는 모든 생성된 구조체와 열거형 인스턴스들, 그리고 그것이 가진 프로퍼티로 가진 어떠한 값 타입들은 코드 상에서 항상 복사된다는 것을 뜻한다.</p>
<blockquote>
<p>NOTE : 배열, 딕셔너리, 문자열과 같이 기본 라이브러리에 정의된 컬렉션들은 복사하는 데 사용되는 비용을 줄이기 위해 최적화를 한다. 복사본을 바로 만드는 대신, 오리지널 인스턴스와 어떠한 복사본이든 저장되어 있는 메모리 공간을 공유한다. 컬렉션형의 어떠한 복사본이 수정되면, 수정 직전에 그 값들이 복사된다. 이 동작은 바로 복사가 되는 것처럼 보이게 한다.</p>
</blockquote>
<pre><code class="language-swift">let hd = Resolution(width: 1920, height: 1080)
var cinema = hd</code></pre>
<p>위 예시는 <code>hd</code>라는 <code>Resolution</code>형 변수를 각각 <code>width = 1920, height = 1080</code>으로 초기화한 뒤, <code>cinema</code> 라는 변수 초기화에 사용한 것이다. <code>Resolution</code>이 구조체이기 때문에 존재하는 인스턴스의 복사본이 <code>cinema</code>에 할당된다. 지금은 <code>hd</code> 와 <code>cinema</code> 가 모두 같은 값을 갖지만, 이 두 인스턴스는 완벽히 다른 것들이다.</p>
<p>다음으로 디지털 영화 상영을 위해 사용되는 2K 해상도로 <code>cinema</code>의 너비 프로퍼티를 바꿔보자.</p>
<pre><code class="language-swift">cinema.width = 2048</code></pre>
<p><code>cinema</code> 의 <code>width</code> 프로퍼티를 확인해보면 다음과 같이 수정된 것을 볼 수 있다.</p>
<pre><code class="language-swift">print(&quot;cinema의 너비는 \(cinema.width) 픽셀이 됐다.&quot;)
// cinema의 너비는 2048 픽셀이 됐다.</code></pre>
<p>하지만 <code>hd</code>의 <code>width</code> 프로퍼티는 여전히 이전의 값을 가지고 있다.</p>
<pre><code class="language-swift">print(&quot;hd의 너비는 \(hd.width) 픽셀이다.&quot;)
// hd의 너비는 1920 픽셀이다.</code></pre>
<p><code>cinema</code> 가 <code>hd</code>의 값을 부여받을 땐 <code>hd</code>의 값이 <code>cinema</code> 인스턴스에 복사된다. 이 결과 두 분리된 인스턴스는 동일한 값을 지니게 된다. 하지만, 이 두 인스턴스는 분리돼있기 때문에 <code>cinema</code>의 너비를 2048로 바꾸는 것이 <code>hd</code> 의 값에는 아무런 영향을 미치지 않는다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/39357fdf-e159-41be-b8a4-f95181d1da89/sharedStateStruct_2x.png" alt=""></p>
<p>열거형에도 동일하게 적용된다.</p>
<pre><code class="language-swift">enum CompassPoint {
        case north, south, east, west
        mutating func turnNorth() {
                self = .north
        }
}

var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection

currentDirection.turnNorth()

print(&quot;현재 방향은 \(currentDirection)이다.&quot;)
print(&quot;저장된 방향은 \(rememberedDirection)이다.&quot;)
// 현재 방향은 north 이다.
// 저장된 방향은 west 이다.</code></pre>
<p><code>rememberedDirection</code>이 <code>currentDirection</code> 의 값을 부여받을 땐 값이 복사된다. <code>currentDirection</code> 의 값을 변경한다고 해서 <code>rememberedDirection</code>의 값도 변경되는 것은 아니다.</p>
<div style="padding-bottom:50px"></div>


<h1 id="클래스는-참조-타입이다">클래스는 참조 타입이다</h1>
<hr>
<p>값 타입과 달리 참조 타입은 복사되지 않는다. 복사본 대신, 같은 인스턴스가 참조된다.</p>
<pre><code class="language-swift">let tenEighty = VideoMode()

tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = &quot;1080i&quot;
tenEighty.frameRate = 25.0</code></pre>
<p>위 예시는 <code>tenEighty</code>라는 이름의 <code>VideoMode</code>의 인스턴스를 선언한 것이다.</p>
<p><code>tenEighty</code> 를 <code>alsoTenEighty</code> 라는 이름의 새 상수에 할당하고, <code>alsoTenEighty</code>의 프레임 레이트를 바꿔보자.</p>
<pre><code class="language-swift">let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0</code></pre>
<p>클래스는 참조 타입이므로 <code>tenEighty</code>와 <code>alsoTenEighty</code>는 같은 <code>VideoMode</code> 의 인스턴스다. 이 둘은 하나의 같은 인스턴스의 두 가지 다른 이름인 것과 같다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/7757125b-8b6a-4148-91e6-5b7ea8ec2544/sharedStateClass_2x.png" alt=""></p>
<p><code>tenEighty</code>의 프레임 레이트를 확인해보면 새로운 프레임 레이트가 <code>30.0</code>으로 업데이트된 것을 확인할 수 있다.</p>
<pre><code class="language-swift">print(&quot;tenEighty의 프레임 레이트는 \(tenEighty.frameRate)이다.&quot;)
// tenEighty의 프레임 레이트는 30.0이다.</code></pre>
<p>이는 참조 타입이 식별하기 더 어렵다는 것을 보여 주는 예시가 된다. <code>tenEighty</code>와 <code>alsoTenEighty</code>가 코드 상에서 멀리 떨어져 있다면, 비디오 모드가 바뀐 부분을 찾기 어려울 것이다. <code>tenEighty</code>를 쓸 때마다 <code>alsoTenEighty</code> 까지 신경 써 줘야 한다는 것이다. 대조적으로, 값 타입은 같은 값들을 주고 받는 코드들이 소스파일 상에서 가까이에 있기 때문에 식별하기 더 쉽다.</p>
<p><code>tenEighty</code>와 <code>alsoTenEighty</code>가 변수가 아닌 상수로 선언되어 있는 것에 주목하자.
그럼에도 불구하고 <code>tenEighty.frameRate</code>와 <code>alsoTenEighty.frameRate</code> 를 수정할 수 있는 것은 그 각각은 자기자신을 바꾸는 것이 아니기 때문이다. <code>tenEighty</code>와 <code>alsoTenEighty</code>는 <code>VideoMode</code> 인스턴스를 &quot;저장&quot;하지 않는다. 대신, 둘은 모두 어딘가에 있을 <code>VideoMode</code> 인스턴스를 참조하고 있을 뿐이다. 이는 해당 <code>VideoMode</code> 인스턴스의 <code>frameRate</code>를 바꾸지, <code>VideoMode</code> 를 참조하고 있는 상수의 값을 바꾸는 것은 아니다.</p>
<blockquote>
<p>말이 어렵지만, 클래스의 인스턴스를 담는 상수의 이름은 단지 생성된 인스턴스를 참조하고 있다는 것을 기억하면 된다. 상수 자체의 값은 일단 저장돼있지 않을 뿐더러 없는 값을 수정하는 일은 하지 않을 것이다. 즉, 상수가 가리키고 있는 인스턴스의 값이 바뀌는 것이기 때문에 <code>let</code> 으로 선언해도 문제가 없는 것.</p>
</blockquote>
<h2 id="식별-연산자">식별 연산자</h2>
<p>클래스는 참조 타입이기 때문에 두 개 이상의 상수와 변수로 하나의 인스턴스를 참조할 수 있다. (구조체와 열거형에선 같은지의 여부가 참이 아닌데, 이는 그것들이 값 타입으로 상수나 변수에 할당될 때, 혹은 함수에 전달될 때 복사가 되기 때문이다.)</p>
<p>가끔 두 상수 혹은 변수가 정확히 같은 클래스의 인스턴스를 참조하고 있는지 찾아내는 것이 유용할 때가 있다. 이를 위해 스위프트는 두 가지 식별 연산자를 제공한다.</p>
<ul>
<li>동일하다 (<code>===</code>)</li>
<li>동일하지 않다 (<code>!==</code>)</li>
</ul>
<p>두 상수 혹은 변수가 동일한 인스턴스를 참조하고 있는지 위의 연산자를 이용해 확인할 수 있다.</p>
<pre><code class="language-swift">if tenEighty === alsoTenEighty {
    print(&quot;tenEighty와 alsoTenEighty는 같은 VideoMode 인스턴스를 참조하고 있다.&quot;)
}

// tenEighty와 alsoTenEighty는 같은 VideoMode 인스턴스를 참조하고 있다.</code></pre>
<p><code>identical(===)</code>과 <code>equal(==)</code>은 명확히 다르다. <code>Identical</code>은 두 클래스 타입의 상수 혹은 변수가 정확히 동일한 클래스 인스턴스를 찹조하고 있다는 것을 뜻한다. <code>Equal</code>은 두 인스턴스가 같거나 같은 값을 가지고 있다는 것을 의미한다.</p>
<p>사용자 정의 구조체와 클래스를 정의할 때 두 인스턴스가 동일한지에 대한 판별은 코드를 짜는 사람에 달려 있다. <code>==</code>와 <code>!=</code> 연산자를 정의하는 과정은 <a href="https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html#ID45">Equivalence Operators</a> 에 정리되어 있다.</p>
<h2 id="포인터">포인터</h2>
<p>C, C++, Objective-C 를 해 봤다면 이 언어들은 메모리 주소를 참조하기 위해 <code>포인터</code>를 쓴다는 점을 알 것이다. 참조 타입의 인스턴스를 참조하는 스위프트의 상수 혹은 변수들은 C의 포인터와 유사한데, 정확히 메모리 주소를 가리키는 포인터는 아니며, 참조하고 있다는 것을 표시하기 위해 애스터리스크(<code>*</code>)를 사용할 필요도 없다.</p>
<p>대신, 이 때의 참조는 스위프트의 어떠한 다른 상수 혹은 변수와 동일하게 정의되어 있다. 표준 라이브러리에선 메모리 주소를 가리키는 포인터를 필요로 할 때 사용할 수 있는 포인터와 버퍼 타입을 제공한다. 이는 <a href="https://developer.apple.com/documentation/swift/swift_standard_library/manual_memory_management">Manual Memory Management</a> 문서를 참조하자.</p>
<hr>
<p>원문 : <a href="https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html">The Swift Programming Language (Swift 5.5) - Structures and Classes</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Xcode | 내 기기로 시뮬레이터 실행이 안 된다!?]]></title>
            <link>https://velog.io/@seri_ous/Xcode-%EB%82%B4-%EA%B8%B0%EA%B8%B0%EB%A1%9C-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88-%EB%90%9C%EB%8B%A4</link>
            <guid>https://velog.io/@seri_ous/Xcode-%EB%82%B4-%EA%B8%B0%EA%B8%B0%EB%A1%9C-%EC%8B%9C%EB%AE%AC%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%8B%A4%ED%96%89%EC%9D%B4-%EC%95%88-%EB%90%9C%EB%8B%A4</guid>
            <pubDate>Sun, 25 Jul 2021 02:58:07 GMT</pubDate>
            <description><![CDATA[<h3 id="기기도-있는데-실제-테스트나-해-보자">기기도 있는데 실제 테스트나 해 보자</h3>
<p>스위프트 공부를 하면서 초간단 앱을 만들어보고 있었는데 어느 날 시뮬레이터가 아닌 내 기기에 깔아서 확인하고 싶은 마음이 들었다.
<img src="https://images.velog.io/images/seri_ous/post/3fd59dc6-f052-4fdf-abd5-4f430c28f1b8/dicjwjgv.jpeg" alt=""></p>
<p>그래서 그냥 USB로 기기를 연결하면 됐던 것 같아서 별 생각 없이 연결했는데 이게 웬 걸, 연결이 안 됐다.</p>
<hr>
<h3 id="xcode-please-reconnect-the-device">Xcode Please reconnect the device</h3>
<p>이 에러 메시지가 계속 나와서 말 그대로 다시 연결하기를 반복했다.</p>
<p>핸드폰과 맥북을 껐다 켜면 된다는 구글 선생님들의 말씀을 따라 각각 기기를 껐다 켜 봤지만 해결되지 않았다.</p>
<div style="margin-bottom: 50px;"></div>

<p>몰까... 하며 귀찮음에 포기하려던 차에, 나와 같은 상황의 블로그 글을 보게 되었다.</p>
<blockquote>
<p><a href="https://oneday0012.tistory.com/237">Xcode Please reconnect the device 문제 해결하기</a></p>
</blockquote>
<p>연결상태의 문제가 아니라, <strong>Xcode 버전과 iOS 버전의 간극</strong>때문이었다니.</p>
<div style="margin-bottom: 50px;"></div>

<p>Xcode의 <strong>[Windows] → [Devices and Simulator]</strong> 메뉴를 실행해보자. 제대로 된 에러 메시지를 볼 수 있다.</p>
<p>정신 놓고 캡쳐도 안 해서 에러 예시는 위 블로그에 들어가면 볼 수 있다.</p>
<div style="margin-bottom: 50px;"></div>

<p>그렇다면 내 iOS 버전을 감당할 수 있는 Xcode 버전을 설치해야 하는데, 지금은 이미 너무 많이 <code>빅 서</code> 화 돼버렸다. 앱스토어에 나와 있는 최신 버전은 업데이트가 간편하지만, OS 최소 사양이 빅 서 였기 때문에 다른 방법을 찾을 수밖에 없었다.</p>
<p>(내 맥북은 2013 버전의 노인이었기 때문에 빅서게이트를 본 이상 차마 OS 업데이트는...)</p>
<p><img src="https://images.velog.io/images/seri_ous/post/235357c2-97b3-4d07-934b-4ff6a46d6983/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.41.00.png" alt=""></p>
<p>다행히, 내 iOS 버전을 감당할 수 있는 Xcode 최소 버전과 내 맥 OS 버전과 맞아떨어졌다.
해당 버전의 다운로드는 아래 사이트에서 했다.</p>
<blockquote>
<p><a href="https://xcodereleases.com/">Xcode Releases</a></p>
</blockquote>
<p>열심히 노인 학대를 한 끝에, 설치에 성공하고 &#39;이젠 되겠지..&#39; 라고 생각했지만 응 안돼~..</p>
<div style="margin-bottom: 50px;"></div>

<hr>
<h3 id="errors-were-encountered-while-preparing-your-device-for-development-please-check-the-devices-and-simulators-window">Errors were encountered while preparing your device for development. Please check the Devices and Simulators Window.</h3>
<p><img src="https://images.velog.io/images/seri_ous/post/8b232adb-fb2d-4caf-aa51-3a3db763ee05/pngwing.com.png" alt=""></p>
<p>다음으로 맞닥뜨린 에러. 보자마자 &#39;시뮬레이터도 업뎃해야하나 설마...?&#39; 라고 생각하고 구글링을 했다.</p>
<p><strong>응 그냥 핸드폰을 껐다 키자.</strong></p>
<p>이젠 되겠지...!!!!!!!!!</p>
<p>응 안돼~</p>
<div style="margin-bottom: 50px;"></div>

<hr>
<h3 id="could-not-launch-projectname"><strong>Could not launch &quot;ProjectName&quot;</strong></h3>
<p>The operation couldn’t be completed. Unable to launch stopwatch. NavigationBarCheck because it has an invalid code signature, inadequate entitlements or its profile has not been explicitly trusted by the user.</p>
<p>이번엔 또 뭐냐 싶어서 다시 구글링을 해 봤고, 다행히 해결이 어렵지 않은 문제였다. 연결할 iOS 기기 설정에서 체크만 해 주면 됐다.</p>
<div style="margin-bottom: 50px;"></div>

<ol>
<li><p>연결할 기기의 <strong>&#39;설정(Setting)&#39; → &#39;일반(General)&#39;</strong></p>
</li>
<li><p><strong>&#39;기기 관리(Device Management)&#39;</strong> 선택</p>
<blockquote>
<p>앞선 두 차례의 에러 메시지 동안 여러 번 봤던 에러 해결 방법이어서 그 때마다 확인 해 봤는데, 이거로 해결 할 수 없는 문제가 아니면 &#39;기기 관리&#39;가 뜨지 않는다.</p>
</blockquote>
<blockquote>
<p>즉, 내가 디바이스에 설정→ 일반에 들어갔는데 기기 관리가 없다면 이 문제가 아닌 것!</p>
</blockquote>
</li>
<li><p><strong>Apple Development: 어쩌구</strong>로 되어 있는 앱 선택</p>
</li>
<li><p><strong>파란 텍스트</strong> 선택하고 팝업에서 <strong>&#39;신뢰&#39;</strong> 선택</p>
</li>
<li><p>끝! 다시 Xcode 로 돌아가 <strong>Run</strong> 해보자.</p>
<div style="margin-bottom: 50px;"></div>

</li>
</ol>
<hr>
<h3 id="작고-소중한-나의-앱이-깔렸읍니다">작고 소중한 나의 앱이 깔렸읍니다..</h3>
<p><img src="https://images.velog.io/images/seri_ous/post/3e16314a-02c1-42b9-b8f2-82ffc2ae8477/IMG_7356E108ED8C-1.jpeg" alt=""></p>
<p>끝!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift | 너비 우선 탐색(BFS), Swift Algorithm Club 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%EB%84%88%EB%B9%84-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89BFS-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%EB%84%88%EB%B9%84-%EC%9A%B0%EC%84%A0-%ED%83%90%EC%83%89BFS-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Fri, 23 Jul 2021 11:57:29 GMT</pubDate>
            <description><![CDATA[<p>✔︎ 이 글은 <a href="https://github.com/raywenderlich/swift-algorithm-club/">Swift Algorighm Club</a> 의 문서를 번역한 글입니다.</p>
<h2 id="너비-우선-탐색breadth-first-search">너비 우선 탐색(Breadth-First Search)</h2>
<hr>
<blockquote>
<p>이 토픽은 <a href="https://www.raywenderlich.com/710-swift-algorithm-club-swift-breadth-first-search">여기</a>서 배울 수 있습니다.</p>
</blockquote>
<p>너비 우선 탐색은 트리나 그래프를 순회하거나 찾는 알고리즘이다. 소스 노드에서 시작해 다음 레벨의 근방 노드로 움직이기 전까지 가장 가까이에 있는 노드부터 탐색해나간다.</p>
<p>너비 우선 탐색은 방향성이 있는 그래프와 방향성이 없는 그래프 모두에 사용될 수 있다.</p>
<h2 id="움직이는-예시">움직이는 예시</h2>
<hr>
<p>다음은 너비 우선 탐색이 그래프에서 어떻게 작동하는지를 보여 준다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/9881b889-4deb-4189-8bdd-e10148bf91a0/AnimatedExample.gif" alt=""></p>
<p>노드를 방문하면 검정으로 칠하자. 그와 동시에 인접한 노드를 큐에 넣는다. 위의 애니메이션에서 인큐(enqueue)됐지만 아직 방문하지 않은 노드를 회색으로 표시했다.</p>
<p>위의 애니메이션 예시를 따라가보자. 소스노드 <code>A</code> 로 시작하고, 이를 큐에 추가한다. 애니메이션에서 <code>A</code> 노드가 회색으로 보이게 되는 것과 같다.</p>
<pre><code class="language-swift">queue.enqueue(A)</code></pre>
<p>큐는 <code>[ A ]</code> 가 된다. 큐의 동작은 다음과 같다. 큐에 노드가 있을 때, 큐의 가장 첫번째 노드를 방문하고, 아직 방문하지 않은 인접한 노드를 곧바로 인큐하는 것이다.</p>
<p>그래프 순회를 시작하기 위해 큐에서 첫번째 노드 <code>A</code> 를 꺼내고 이를 검정색으로 칠한다. 그리고 인접한 노드 <code>B</code> 와 <code>C</code> 를 인큐한다. 이 동작은 두 노드를 회색으로 만든다.</p>
<pre><code class="language-swift">queue.dequeue()   // A
queue.enqueue(B)
queue.enqueue(C)</code></pre>
<p>큐는 이제 <code>[ B, C ]</code> 가 된다. <code>B</code> 를 디큐(dequeue)하고, <code>B</code> 의 인접 노드인 <code>D</code> 와 <code>E</code> 를 인큐한다.</p>
<pre><code class="language-swift">queue.dequeue()   // B
queue.enqueue(D)
queue.enqueue(E)</code></pre>
<p>큐는 <code>[ C, D, E ]</code> 가 된다. <code>C</code> 를 디큐하고, <code>C</code> 의 인접 노드인 <code>F</code> 와 <code>G</code> 를 인큐한다.</p>
<pre><code class="language-swift">queue.dequeue()   // C
queue.enqueue(F)
queue.enqueue(G)</code></pre>
<p>큐는 <code>[ D, E, F, G ]</code> 가 된다. 인접한 노드가 없는 <code>D</code> 를 디큐한다.</p>
<pre><code class="language-swift">queue.dequeue()   // D</code></pre>
<p>큐는 <code>[ E, F, G ]</code> 고, <code>E</code> 를 디큐하고 그것의 유일한 인접 노드인 <code>H</code> 를 인큐한다. <code>E</code> 의 인접 노드에는 <code>B</code> 도 포함돼있지만, 이미 <code>B</code> 는 방문했기 때문에 큐에 다시 집어넣지 않는다는 점을 기억해야 한다.</p>
<pre><code class="language-swift">queue.dequeue()   // E
queue.enqueue(H)</code></pre>
<p>큐는 <code>[ F, G, H ]</code> 가 된다. 방문한 적 없는 인접한 노드가 없는 <code>F</code> 를 디큐한다.</p>
<pre><code class="language-swift">queue.dequeue()   // F</code></pre>
<p>큐는 <code>[ G, H ]</code> 가 된다. 방문한 적 없는 인접한 노드가 없는 <code>G</code> 를 디큐한다.</p>
<pre><code class="language-swift">queue.dequeue()   // G</code></pre>
<p>큐는 <code>[ H ]</code> 가 된다. 방문한 적 없는 인접한 노드가 없는 <code>H</code> 를 디큐한다.</p>
<pre><code class="language-swift">queue.dequeue()   // H</code></pre>
<p>큐는 이제 비게 되고, 이는 모든 노드들을 다 탐색했다는 뜻이 된다. 방문한 노드들의 순서는 <code>A</code> , <code>B</code> , <code>C</code> , <code>D</code> , <code>E</code> , <code>F</code> , <code>G</code> , <code>H</code> 이다.</p>
<p>이를 트리로 나타내면 다음과 같다.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/0e475cb8-9df3-476b-9a2f-c2f2014f580c/TraversalTree.png" alt=""></p>
<p>부모 노드는 해당 노드를 &quot;발견한&quot; 노드를 말한다. 트리의 루트는 너비 우선 탐색을 시작한 노드가 된다.</p>
<p>unweighted 그래프이기 때문에 이 트리는 시작 노드부터 모든 다른 노드들까지의 거리가 가장 짧은 것을 의미한다. 그러므로 너비 우선 탐색은 그래프 상의 두 노드 간 최단 거리를 찾을 때 사용하는 방법 중 하나이다.</p>
<blockquote>
<p>그래프 상에서 edge(여기선 그냥 노드라고 생각)들이 모두 동일한 무게를 가지고 있을 경우 unweighted 라고 한다. &#39;최단 거리&#39;의 맥락에서 봤을 때, 각 레벨의 노드들이 각각 동일한 만큼 떨어져 있다고 생각하면 될 것 같다.</p>
</blockquote>
<h2 id="코드">코드</h2>
<hr>
<p>큐를 사용한 간단한 너비 우선 탐색 구현은 다음과 같다.</p>
<div style="color:gray; font-style:italic">해당 코드는 너비 우선 탐색의 알고리즘 동작을 보여 주는 것으로, Swift에는 Graph, Node, Queue와 같은 자료형이 정의되어 있지 않다. 실행하고 싶다면, 모든 요소들을 다운받을 수 있는 원문을 참조하자.</div>

<pre><code class="language-swift">func breadthFirstSearch(_ graph: Graph, source: Node) -&gt; [String] {
    var queue = Queue&lt;Node&gt;()
    queue.enqueue(source)

    var nodesExplored = [source.label]
    source.visited = true

    while let node = queue.dequeue() {
        for edge in node.neighbors {
            let neighborNode = edge.neighbor
            if !neighborNode.visited {
                queue.enqueue(neighborNode)
                neighborNode.visited = true
                nodesExplored.append(neighborNode.label)
            }
        }
    }

    return nodesExplored
}</code></pre>
<p>큐에 노드가 있는 동안, 첫번째 노드를 방문하고 그와 동시에 아직 방문하지 않은 인접 노드들을 인큐한다.</p>
<p>코드를 플레이그라운드에서 실행시켜보자.</p>
<pre><code class="language-swift">let graph = Graph()

let nodeA = graph.addNode(&quot;a&quot;)
let nodeB = graph.addNode(&quot;b&quot;)
let nodeC = graph.addNode(&quot;c&quot;)
let nodeD = graph.addNode(&quot;d&quot;)
let nodeE = graph.addNode(&quot;e&quot;)
let nodeF = graph.addNode(&quot;f&quot;)
let nodeG = graph.addNode(&quot;g&quot;)
let nodeH = graph.addNode(&quot;h&quot;)

graph.addEdge(nodeA, neighbor: nodeB)
graph.addEdge(nodeA, neighbor: nodeC)
graph.addEdge(nodeB, neighbor: nodeD)
graph.addEdge(nodeB, neighbor: nodeE)
graph.addEdge(nodeC, neighbor: nodeF)
graph.addEdge(nodeC, neighbor: nodeG)
graph.addEdge(nodeE, neighbor: nodeH)
graph.addEdge(nodeE, neighbor: nodeF)
graph.addEdge(nodeF, neighbor: nodeG)

let nodesExplored = breadthFirstSearch(graph, source: nodeA)
print(nodesExplored)</code></pre>
<p>이는 <code>[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;, &quot;f&quot;, &quot;g&quot;, &quot;h&quot;]</code> 과 같은 결과를 도출하게 된다.</p>
<h2 id="bfs-뭐에-좋은데">BFS, 뭐에 좋은데?</h2>
<hr>
<p>너비 우선 탐색 알고리즘은 많은 문제를 푸는 데 사용된다. 예를 들어,</p>
<ul>
<li>소스 노드(처음 노드)와 다른 노드들 간의 최단거리를 계산할 때(단, unweighted 그래프에서만)</li>
<li>unweighted 그래프에서 최소 신장 트리(MST, Minimum Spanning Tree)를 계산할 때</li>
</ul>
<p>와 같은 경우가 있겠다.</p>
<p><em>Written by Chris Pilcher and Matthijs Hollemans</em></p>
<p><em>원문 <a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Breadth-First%20Search">https://github.com/raywenderlich/swift-algorithm-club/tree/master/Breadth-First Search</a></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift | 삽입 정렬, Swift Algorithm Club 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%EC%82%BD%EC%9E%85-%EC%A0%95%EB%A0%AC-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%EC%82%BD%EC%9E%85-%EC%A0%95%EB%A0%AC-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Fri, 16 Jul 2021 04:33:56 GMT</pubDate>
            <description><![CDATA[<p>✔︎ 이 글은 <a href="https://github.com/raywenderlich/swift-algorithm-club/">Swift Algorithm Club</a> 문서를 번역한 것입니다.</p>
<h2 id="삽입-정렬">삽입 정렬</h2>
<hr>
<p>목표: 배열을 오름차순(혹은 내림차순)으로 정렬하기</p>
<p>숫자 배열이 주어지고, 이를 순서대로 정렬해야 한다. 이 때 삽입 정렬 알고리즘은 다음과 같이 작동한다.</p>
<ul>
<li><p>숫자를 파일에 놓는다. 이 파일은 정렬되지 않은 상태다.</p>
<p>  <strong><a href="https://en.wikipedia.org/wiki/Pile_(abstract_data_type)">pile</a></strong> : 데이터를 순서대로 정렬하기 위해 사용되는 <a href="https://ko.wikipedia.org/wiki/%EC%B6%94%EC%83%81_%EC%9E%90%EB%A3%8C%ED%98%95">추상 자료형</a>이다.</p>
</li>
<li><p>파일에서 숫자를 하나 뽑는다. 뽑는 숫자는 어떤거든 상관 없지만, 파일의 맨 위부터 뽑는 것이 가장 쉽다.</p>
</li>
<li><p>뽑은 숫자를 새 배열에 추가한다.</p>
</li>
<li><p>정렬되지 않은 파일에서 새 숫자를 뽑아 앞선 새 배열에 추가한다. 이 숫자는 처음 뽑았던 숫자 앞과 뒤에 모두 올 수 있으며, 이를 통해 두 숫자는 정렬되게 된다.</p>
</li>
<li><p>파일에서 새 숫자를 뽑아 배열의 알맞은 위치에 배치해 정렬한다.</p>
</li>
<li><p>파일에 숫자가 없을 때까지 반복한다. 파일은 비게 되고, 배열은 정렬된다.</p>
</li>
</ul>
<p>파일에서 숫자를 뽑아 배열의 알맞은 위치에 추가해 정렬하기 때문에 이를 &quot;삽입&quot;정렬이라고 부른다.</p>
<h2 id="예시">예시</h2>
<hr>
<p>정렬해야 하는 수가 <code>[ 8, 3, 5, 4, 6]</code> 이라 해 보자. 이는 우리가 가진 정렬되지 않은 파일이다.</p>
<p>처음 숫자 <code>8</code> 을 뽑아서 새 배열에 추가한다. 배열엔 아무것도 없으므로 쉽게 할 수 있다. 정렬된 배열은 <code>[ 8 ]</code> 이 되고, 파일은 <code>[ 3, 5, 4, 6 ]</code> 이다.</p>
<p>파일에서 다음 숫자인 <code>3</code> 을 뽑아서 정렬된 배열에 추가한다. 이는 <code>8</code> 앞으로 가야 하고, 배열은 <code>[ 3, 8 ]</code> 이 되고, 파일은 <code>[ 5, 4, 6 ]</code> 으로 줄어든다.</p>
<p>다음 숫자 <code>5</code> 를 뽑아 배열에 추가한다. 이는 <code>3</code> 과 <code>8</code> 사이에 들어간다. 정렬된 배열은 <code>[ 3, 5, 8 ]</code> , 파일은 <code>[ 4, 6 ]</code> 이다.</p>
<p>파일이 빌 때까지 이 프로세스를 반복한다.</p>
<h2 id="제자리-정렬in-place-sort">제자리 정렬(In-place sort)</h2>
<hr>
<blockquote>
<p><strong><a href="https://en.wikipedia.org/wiki/In-place_algorithm">*In-place sort</a></strong> : 추가적인 메모리 공간이 거의 안 드는 정렬*</p>
</blockquote>
<p>위에서 한 설명은 정렬되지 않은 파일과 정렬된 숫자를 담는 두 배열을 필요로 하는 것처럼 보인다.</p>
<p>하지만 분리된 배열을 만들지 않고 제자리에서 삽입 정렬을 할 수 있다. 단지 배열의 어떤 부분이 정렬됐고, 어떤 부분이 정렬되지 않았는지 잘 따라가기만 하면 된다.</p>
<p>초기에 배열은 <code>[ 8, 3, 5, 4, 6 ]</code> 이고, <code>|</code> 바는 정렬된 부분이 끝나고 파일이 시작되는 부분을 표현한다.</p>
<pre><code class="language-swift">[| 8, 3, 5, 4, 6 ]</code></pre>
<p>이는 정렬된 배열이 비어 있고, 파일이 <code>8</code> 로 시작한다는 것을 보여 준다.</p>
<p>첫 번째 숫자를 가지고 정렬을 하면 다음과 같아진다.</p>
<pre><code class="language-swift">[ 8 | 3, 5, 4, 6 ]</code></pre>
<p>정렬된 부분은 <code>[ 8 ]</code> 이고, 파일은 <code>[ 3, 5, 4, 6 ]</code> 이 된다. <code>|</code> 바가 오른쪽으로 한 칸 옮겨진 것이다.</p>
<p>아래는 정렬에 따라 배열의 요소가 어떻게 변화하는지를 보여 준다.</p>
<pre><code class="language-swift">[| 8, 3, 5, 4, 6 ]
[ 8 | 3, 5, 4, 6 ]
[ 3, 8 | 5, 4, 6 ]
[ 3, 5, 8 | 4, 6 ]
[ 3, 4, 5, 8 | 6 ]
[ 3, 4, 5, 6, 8 |]</code></pre>
<p>각 과정에서 <code>|</code> 바는 한칸씩 움직인다. 위에서 볼 수 있듯, <code>|</code> 앞의 부분은 항상 정렬돼있다. 왼쪽에 정렬되지 않은 숫자가 없이 파일이 빌 때까지 파일은 줄어들고, 정렬된 배열은 하나씩 늘어난다.</p>
<h2 id="어떻게-추가하나">어떻게 추가하나</h2>
<hr>
<p>매 단계마다 정렬되지 않은 파일에서 맨 앞 숫자를 뽑아 정렬된 배열의 적절한 위치에 추가한다. 그 수를 알맞은 곳에 넣어 배열의 시작이 정렬된 상태로 있도록 만든다. 이게 어떻게 동작할까?</p>
<p>우리가 몇 가지 요소를 정렬했고, 그래서 배열의 상태가 아래와 같다고 가정하자.</p>
<pre><code class="language-swift">[ 3, 5, 8 | 4, 6 ]</code></pre>
<p>다음으로 정렬할 수는 <code>4</code> 다. 우리는 정렬된 <code>[ 3, 5, 8 ]</code> 배열의 어딘가에 이 수를 넣어야 한다.</p>
<p>한 가지 방법이 있다. 앞 요소인 <code>8</code> 을 보자.</p>
<pre><code class="language-swift">[ 3, 5, 8, 4 | 6 ]
        ^</code></pre>
<p><code>4</code> 보다 큰가? 그렇다. 그러므로 <code>4</code> 는 <code>8</code> 의 앞에 와야 한다. 아래와 같은 배열을 만들기 위해 두 수를 교환하자.</p>
<pre><code class="language-swift">[ 3, 5, 4, 8 | 6 ]
        &lt;--&gt;
       swapped</code></pre>
<p>아직 끝나지 않았다. 새로운 앞 숫자인 <code>5</code> 또한 <code>4</code> 보다 크다. 이 두 수 또한 교환하자.</p>
<pre><code class="language-swift">[ 3, 4, 5, 8 | 6 ]
     &lt;--&gt;
    swapped</code></pre>
<p>다시 앞 숫자를 보자. <code>3</code> 이 <code>4</code> 보다 큰가? 아니다. <code>4</code> 는 제 자리를 찾았고, 앞의 배열은 다시 정렬됐다.</p>
<p>이것은 다음 파트에서 보게 될 삽입 정렬의 내부 반복을 설명한 것이다. 교환을 통해 파일의 맨 처음 수를 정렬된 부분에 추가하게 된다.</p>
<h2 id="코드">코드</h2>
<hr>
<p>스위프트로 삽입 정렬을 구현한 코드는 다음과 같다.</p>
<pre><code class="language-swift">func insertionSort(_ array: [Int]) -&gt; [Int] {
    var sortedArray = array
    for index in 1..&lt;sortedArray.count {
        var currentIndex = index
        while currentIndex &gt; 0 &amp;&amp; sortedArray[currentIndex] &lt; sortedArray[currentIndex - 1] {
            sortedArray.swapAt(currentIndex - 1, currentIndex)
            currentIndex -= 1
        }
    }
    return sortedArray
}</code></pre>
<p>이 코드를 플레이그라운드에 넣고 다음과 같이 실행해보자.</p>
<pre><code class="language-swift">let list = [ 10, -1, 3, 9, 2, 27, 8, 5, 1, 3, 0, 26 ]
insertionSort(list)</code></pre>
<p>코드가 작동하는 방식은 다음과 같다.</p>
<ol>
<li>배열의 복사본을 만든다. 매개변수 <code>array</code> 를 직접적으로 바꿀 수 없기 때문에 이 작업이 필요하다. 스위프트가 가진 <code>sorted()</code> 와 같이, <code>insertionSort()</code> 함수는 원래의 배열을 복사해 정렬한 것을 반환한다.</li>
<li>이 함수에는 두 개의 반복문이 있다. 밖의 반복문은 배열의 각 요소에 대해서 적용되는 것으로, 파일에서 맨 앞 수를 뽑는 역할을 한다. 변수 <code>x</code> 는 정렬된 부분의 끝이자 파일이 시작되는 <strong>인덱스</strong>(앞에서 <code>|</code> 바가 위치하는 곳)를 의미한다. 0부터 <code>x</code> 까지에 해당하는 배열의 앞 부분은 언제나 정렬돼야 한다는 걸 기억하자. 나머지, <code>x</code> 부터 마지막 요소는 정렬되지 않은 파일이다.</li>
<li>안의 반복문은 <code>x</code> 위치에 있는 <strong>요소</strong>에 대해서 확인한다. 이는 파일의 맨 앞 숫자이고, 이는 자신의 앞 요소보다 작을 수 있다. 내부의 반복문은 정렬된 배열을 뒤에서부터 확인 해 나간다. 앞 숫자가 클 때마다 두 수를 교환한다. 내부의 반복문이 끝나면 배열의 앞 부분은 다시 정렬되고, 정렬된 부분의 요소는 하나 늘게 된다.</li>
</ol>
<blockquote>
<p>주의: 밖의 반복문은 0이 아닌 1 인덱스부터 시작한다. 파일에서 정렬된 부분으로 제일 처음 수를 옮기는 것은 아무 것도 바뀌지 않으므로 건너뛰어도 된다.</p>
</blockquote>
<h2 id="교환-멈춰">교환 멈춰!</h2>
<hr>
<p>위의 삽입 정렬도 잘 작동하지만, <code>swap()</code> 을 호출하지 않음으로써 조금 더 빠르게 바꿀 수 있다.</p>
<p>위에서 우리는 다음 요소를 정렬시키기 위해 숫자들을 교환했다.</p>
<pre><code class="language-swift">[ 3, 5, 8, 4 | 6 ]
        &lt;--&gt;
        swap

[ 3, 5, 4, 8 | 6 ]
     &lt;--&gt;
     swap</code></pre>
<p>앞의 요소와 매번 교환하는대신, 모든 요소들을 한칸씩 오른쪽으로 옮기고, 정렬할 숫자를 해당 위치에 복사하는 것을 사용해보자.</p>
<pre><code class="language-swift">[ 3, 5, 8, 4 | 6 ]   4 기억하기
           *

[ 3, 5, 8, 8 | 6 ]   8을 오른쪽으로 옮기기
        ---&gt;

[ 3, 5, 5, 8 | 6 ]   5을 오른쪽으로 옮기기
     ---&gt;

[ 3, 4, 5, 8 | 6 ]   4를 해당 위치에 복사하기
     *</code></pre>
<p>코드상에서 다음과 같이 나타낼 수 있다.</p>
<pre><code class="language-swift">func insertionSort(_ array: [Int]) -&gt; [Int] {
    var sortedArray = array
    for index in 1..&lt;sortedArray.count {
        var currentIndex = index
        let temp = sortedArray[currentIndex]
        while currentIndex &gt; 0 &amp;&amp; temp &lt; sortedArray[currentIndex - 1] {
            sortedArray[currentIndex] = sortedArray[currentIndex - 1]         //1
            currentIndex -= 1
        }
        sortedArray[currentIndex] = temp                                    //2
    }
    return sortedArray
}</code></pre>
<p><code>//1</code> 번 라인에서 앞의 요소들이 한칸씩 옮겨진다. 내부 반복문의 마지막에, <code>y</code> 가 새 숫자가 위치할 인덱스를 의미하고, <code>//2</code> 번 라인에서 이 숫자를 그 장소에 복사한다.</p>
<h2 id="제네릭으로-만들어보자">제네릭으로 만들어보자</h2>
<hr>
<blockquote>
<p><a href="https://docs.swift.org/swift-book/LanguageGuide/Generics.html">*제네릭(generic)</a> : 데이터 타입에 관계 없이 어떤 요소든 사용할 수 있도록 만드는 것(타입)*</p>
</blockquote>
<p>숫자가 아닌 다른 것들도 정렬할 수 있으면 좋을 것이다. 배열의 데이터 타입을 제네릭으로 설정하고, 사용자 정의 함수(혹은 클로저)를 사용하여 <code>&lt;</code> 비교 연산자의 기능을 만들 수 있다. 코드의 두 부분만 바꾸면 된다.</p>
<p>함수의 시그니처(정의)는 다음과 같이 바뀐다.</p>
<pre><code class="language-swift">func insertionSort&lt;T&gt;(_ array: [T], _ isOrderedBefore: (T, T) -&gt; Bool) -&gt; [T] {</code></pre>
<p>배열은 <code>[T]</code> 형 배열이 되고, 이 때의 <code>T</code> 는 제네릭 타입을 담는다. 이제 <code>insertionSort()</code> 는 숫자, 스트링 등 어떤 타입의 배열이든 모두 처리할 수 있다.</p>
<p>새로운 매개변수 <code>isOrderedBefore: (T, T) -&gt; Bool</code> 은 두 개의 <code>T</code> 객체를 인자로 받아 첫 번째 객체가 두 번째 객체 전에 위치하면 참을, 그렇지 않으면 거짓을 반환하는 함수이다. 이는 사실 스위프트의 <code>sort()</code> 함수가 하는 동작과 같다.</p>
<p>바뀔 다음 부분은 코드의 내부 반복문인데, 다음과 같이 바뀐다.</p>
<pre><code class="language-swift">        while y &gt; 0 &amp;&amp; isOrderedBefore(temp, a[y-1]) {</code></pre>
<p><code>temp &lt; a[y - 1]</code> 과 같이 쓰는 대신, <code>isOrderedBefore()</code> 함수를 호출하는 것이다. 이는 완벽히 동일한 동작을 하는데, 거기에 더해 숫자가 아닌 다른 어떤 객체든 비교할 수 있게 된다.</p>
<p>이를 플레이그라운드 상에서 다음과 같이 실행해보자.</p>
<pre><code class="language-swift">let numbers = [ 10, -1, 3, 9, 2, 27, 8, 5, 1, 3, 0, 26 ]
insertionSort(numbers, &lt;)
insertionSort(numbers, &gt;)</code></pre>
<p><code>&lt;</code> 와 <code>&gt;</code> 는 각각 오름차순인지 내림차순인지 판단하게 해 준다.</p>
<p>물론, 스트링과 같은 다른 타입의 것들을 정렬할 수 있고,</p>
<pre><code class="language-swift">let strings = [ &quot;b&quot;, &quot;a&quot;, &quot;d&quot;, &quot;c&quot;, &quot;e&quot; ]
insertionSort(strings, &lt;)</code></pre>
<p>그보다 더 복잡한 객체들도 가능하다.</p>
<pre><code class="language-swift">let objects = [ obj1, obj2, obj3, ... ]
insertionSort(objects) { $0.priority &lt; $1.priority }</code></pre>
<p>위에서 클로저는 <code>insertionSort()</code> 가 객체들의 <code>priority</code> 에 따라 정렬하게 한다.</p>
<p>삽입 정렬은 <em>안정 정렬</em>이다. 정렬이 안정적이라는 것은, 같은 요소가 있을 때 정렬 전과 정렬 후 두 요소의 순서가 같다는 것을 의미한다. 이는 숫자나 스트링타입과 같은 단순한 값들에 대해서는 그다지 중요하지 않지만, 더 복잡한 것들에 대해서는 중요할 수 있다. 위의 예시에서 두 객체가 같은 <code>priority</code> 를 갖는다면, 각각의 다른 프로퍼티와 관련 없이 두 객체는 교환되지 않는다.</p>
<h2 id="성능">성능</h2>
<hr>
<p>삽입 정렬은 배열이 정렬돼있으면 정말 빠르다. 당연한 듯 들리겠지만, 모든 탐색 알고리즘이 그런 것은 아니다. 현실에서 많은 데이터는 전체적으로는 아니더라도, 일정 부분 정렬돼있으므로 삽입 정렬은 이 경우에 꽤 좋은 성능을 낸다.</p>
<p>최악의 경우, 그리고 평균적인 경우 삽입 정렬의 성능은 O(n^2) 이다. 함수에 두 개의 중첩된 반복문이 있기 때문이다. 퀵소트와 합병 정렬과 같은 다른 정렬 알고리즘에선 O(n log n) 의 시간복잡도를 갖는데, 이는 큰 수에 대해 더 빨리 동작할 수 있다.</p>
<p>삽입 정렬은 작은 배열을 정렬할 때 가장 빠르다. 몇몇 표준 라이브러리에선 파티션의 크기가 10 이하일 때 정렬 함수를 퀵소트에서 삽입 정렬로 바꾸어 사용하기도 한다.</p>
<p>스위프트의 내장된 <code>sort()</code> 함수와 <code>insertionSort()</code> 를 비교해봤다. 100개 정도의 요소가 들어 있는 배열에 대해서는 그 속도가 거의 차이가 없었다. 하지만, 배열의 크기가 커질수록 O(n^2) 가 O(n log n) 에 비해 훨씬 느려졌고, 삽입 정렬이 그 속도를 따라갈 수 없었다.</p>
<h2 id="참고">참고</h2>
<hr>
<p><a href="https://en.wikipedia.org/wiki/Insertion_sort">삽입 정렬에 대한 위키피디아</a></p>
<p><em>Written for Swift Algorithm Club by Matthijs Hollemans</em>
<em>원문 <a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Insertion%20Sort">https://github.com/raywenderlich/swift-algorithm-club/tree/master/Insertion%20Sort</a></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift| 큐, Swift Algorithm Club 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%ED%81%90-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%ED%81%90-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Wed, 14 Jul 2021 12:39:28 GMT</pubDate>
            <description><![CDATA[<p><em>✔︎ 이 글은 <a href="https://github.com/raywenderlich/swift-algorithm-club/">Swift Algorithm Club</a> 의 문서를 번역한 것입니다.</em></p>
<h2 id="큐">큐</h2>
<hr>
<blockquote>
<p>이 주제의 튜토리얼은 <a href="https://www.raywenderlich.com/148141/swift-algorithm-club-swift-queue-data-structure">이 곳</a>에서</p>
</blockquote>
<p>큐는 뒤에서부터 새로운 요소를 추가하고, 앞에서부터 삭제하는 리스트이다. 이는 당신이 처음으로 큐에 집어넣는(enqueue) 원소가 처음으로 큐에서 추출하는(dequeue) 원소임을 보장한다. 선입선출을 뜻한다!</p>
<p>이게 왜 필요할까? 많은 알고리즘에서 당신은 무언가를 일시적인 리스트에 추가했다가 나중에 꺼내고 싶을 것이다. 때때로 이것들을 추가하고 삭제하는 순서가 중요할 수 있다.</p>
<p>큐는 선입선출(FIFO-First In First Out)의 구조를 따른다. 처음에 추가한 요소가 처음으로 나오게 되는 것이다. 당연한 말이다! (비슷한 자료구조인 스택은, 후입선출 LIFO-Last In First Out의 구조로 되어 있다.)</p>
<h2 id="예시">예시</h2>
<hr>
<p>아래는 수를 큐에 집어넣는(enqueue) 예시이다.</p>
<pre><code class="language-swift">queue.enqueue(10)</code></pre>
<p>큐는 <code>[ 10 ]</code> 이 된다. 다음 수를 집어넣어보자.</p>
<pre><code class="language-swift">queue.enqueue(3)</code></pre>
<p>큐는 <code>[ 10, 3 ]</code> 이 된다. 또 다른 수를 하나 집어넣어보자.</p>
<pre><code class="language-swift">queue.enqueue(57)</code></pre>
<p>큐는 <code>[ 10, 3, 57 ]</code> 이 된다. 여기서 처음 요소를 큐의 맨 앞에서 꺼내(dequeue)보자.</p>
<pre><code class="language-swift">queue.dequeue()</code></pre>
<p>우리가 처음 추가한 숫자인 <code>10</code> 을 반환한다. 큐는 <code>[ 3, 57 ]</code> 이다. 모든 요소가 하나씩 옮겨졌다.</p>
<pre><code class="language-swift">queue.dequeue()</code></pre>
<p>위 코드 실행시 <code>3</code> 이 반환되고 다음 디큐(dequeue)시 <code>57</code> 을 반환하며 이 방식이 계속된다. 큐가 비어있다면, 큐에서 요소를 빼내는 작업(dequeue)으로 <code>nil</code> 이 반환되거나, 어떤 구현 코드에서는 오류 메시지를 보여 줄 것이다.</p>
<blockquote>
<p><strong>주의</strong> : 큐는 항상 최선의 선택이 아닐 수 있다. 아이템이 추가되고 삭제되는 순서가 중요하지 않다면, 큐 대신 <a href="https://velog.io/@seri_ous/Swift-%EC%8A%A4%ED%83%9D-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD">스택</a>을 사용할 수 있다. 스택이 큐보다 단순하고 빠르다.</p>
</blockquote>
<h2 id="코드">코드</h2>
<hr>
<p>다음은 스위프트로 큐를 구현한 단순한 코드이다. 이는 큐에 집어넣기(enqueue), 큐에서 빼내기(dequeue), 맨 앞 요소 보기(peek) 하기 위한 배열을 새롭게 만든 것과 같다.</p>
<blockquote>
<p>wrapper 에 대한 설명 : <a href="https://velog.io/@seri_ous/Swift-%EC%8A%A4%ED%83%9D-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD">stack</a> 참고</p>
</blockquote>
<pre><code class="language-swift">public struct Queue&lt;T&gt; {
    fileprivate var array = [T]()

    public var isEmpty: Bool {
        return array.isEmpty
    }

    public var count: Int {
        return array.count
    }

    public mutating func enqueue(_ element: T) {
        array.append(element)
    }

    public mutating func dequeue() {
        if isEmpty {
            return nil
        } else {
            return array.removeFirst()
        }
    }

    public var front: T? {
        return array.first
    }
}</code></pre>
<p>이 큐는 잘 작동하지만, 최적의 코드는 아니다.</p>
<p>큐에 추가하는(enqueue) 것은 <strong>O(1)</strong> 만큼의 연산이 되는데, 배열의 마지막에 요소를 추가하는 것은 배열의 크기와 상관없이 항상 같은 시간이 들기 때문이다.</p>
<p>왜 요소를 추가하는 것이 항상 O(1) 혹은 상수 시간(O(1)과 동일 표기)이 드는지 궁금할 것이다. 이는 스위프트의 배열이 항상 마지막에 빈 공간을 가지고 있기 때문이다.</p>
<p>다음과 같이 실행한다면,</p>
<pre><code class="language-swift">var queue = Queue&lt;String&gt;()
queue.enqueue(&quot;Ada&quot;)
queue.enqueue(&quot;Steve&quot;)
queue.enqueue(&quot;Tim&quot;)</code></pre>
<p>배열은 다음과 같이 보일 것이다.</p>
<pre><code class="language-swift">[ &quot;Ada&quot;, &quot;Steve&quot;, &quot;Tim&quot;, xxx, xxx, xxx ]</code></pre>
<p><code>xxx</code> 로 표시된 공간은 아직 채워지지 않았지만 예약된 메모리 공간이다. 새로운 요소를 추가할 때 사용되지 않은 곳을 덮어쓰게 될 것이다.</p>
<pre><code class="language-swift">[ &quot;Ada&quot;, &quot;Steve&quot;, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx ]</code></pre>
<p>이는 하나의 공간에서 다른 곳으로 메모리를 복사하는 것이므로 상수 시간만큼의 연산 시간이 든다.</p>
<p>배열의 끝에는 한정된 수의 공간이 남아 있다. 마지막 <code>xxx</code> 까지 사용된 상태에서, 다른 요소를 추가하고 싶다면, 배열은 더 많은 공간을 만들기 위해 크기를 조정해야 한다.</p>
<p>크기를 조정하는 것은 새로운 메모리를 할당하고, 원래 있던 모든 데이터를 새로운 배열에 복사하는 작업을 포함한다. 이는 비교적으로 느린 O(n) 만큼의 시간을 갖는다. 이는 가끔 일어나는 일이므로, 배열의 끝에 요소를 추가하는 것은 평균적으로, 그리고 분할상환분석상 O(1)의 연산 시간을 갖는다.</p>
<blockquote>
<p><em>분할상환분석(amortized analysis) 은 주어진 알고리즘의 시간 복잡도나 프로그램을 수행하는데 소요되는 시간 또는 메모리같은 자원 사용량을 분석하기 위해 사용하는 기법이다. 수행된 모든 연산에 대해 자료구조 연산이 어떤 시퀀스를 수행하는데 필요한 시간의 평균을 구한다. 확률이 포함되지 않으므로 평균비용 분석과는 다르며, 최악의 경우에도 각 연산의 평균 수행성능을 보장한다.</em></p>
</blockquote>
<p>큐에서 제거하는(dequeue) 일은 조금 다르다. 큐에서 요소를 제거하기 위해서 우리는 배열의 첫번째부터 원소를 제거해야 한다. 이는 항상 O(n) 만큼의 연산을 하는데, 배열에 남아 있는 모든 요소들을 메모리 상에서 옮겨야 하기 때문이다.</p>
<p>우리의 예시에서 첫번째 요소 <code>&quot;Ada&quot;</code> 를 제거하면 <code>&quot;Steve&quot;</code> 를 <code>&quot;Ada&quot;</code> 의 자리로, <code>&quot;Tim&quot;</code> 을 <code>&quot;Steve&quot;</code> 의 자리로, <code>&quot;Grace&quot;</code> 를 <code>&quot;Tim&quot;</code> 의 자리로 복사하게 된다.</p>
<pre><code class="language-swift">before   [ &quot;Ada&quot;, &quot;Steve&quot;, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx ]
                   /       /      /
                  /       /      /
                 /       /      /
                /       /      /
 after   [ &quot;Steve&quot;, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx, xxx ]</code></pre>
<p>모든 요소들을 메모리에서 옮기는 것은 항상 O(n)의 시간복잡도를 갖는다. 그러므로 우리가 구현한 단순한 큐에서 큐에 추가하는 것(enqueue)은 효율적이지만, 큐에서 제거하는 것(dequeue)은 조금 더 생각해볼만 하다.</p>
<h2 id="더-효율적인-큐">더 효율적인 큐</h2>
<hr>
<p>큐에서 제거하는 것(dequeue)을 효율적으로 만들기 위해서 우리는 새로운 공간을 예약해놓을 수 있는데, 이번엔 배열의 앞쪽에 만들어 놓는다. 우리는 이 코드를 우리 스스로 짜야 하는데, 스위프트가 이를 기본적으로 제공해주지 않기 때문이다.</p>
<p>우리가 큐에서 요소를 제거할 때마다(dequeue) 배열의 요소들을 앞으로 옮기(느림)지 않고 빈 배열의 위치를 저장(빠름)하는 것이 더 효율적인 큐의 중심 아이디어다. <code>&quot;Ada&quot;</code> 를 큐에서 제거(dequeue)하고 난 뒤 배열은 다음과 같다.</p>
<pre><code class="language-swift">[ xxx, &quot;Steve&quot;, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx ]</code></pre>
<p><code>&quot;Steve&quot;</code> 를 큐에서 제거(dequeue)하고 나면 배열은 이렇게 된다.</p>
<pre><code class="language-swift">[ xxx, xxx, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx ]</code></pre>
<p>앞에 남겨진 빈 공간들은 절대 재사용되지 않으므로 주기적으로 배열을 잘라 남아 있는 요소들을 앞으로 옮길 수 있게 할 수 있다.</p>
<pre><code class="language-swift">[ &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx, xxx, xxx ]</code></pre>
<p>위와 같은 자르기는 메모리를 옮기므로 <strong>O(n)</strong> 만큼의 시간이 든다. 하지만 이는 한 번씩 일어나는 일이므로 큐에서 빼내는 작업(dequeue)은 평균적으로 <strong>O(1)</strong> 의 시간을 갖는다.</p>
<p>효율적인 버전의 <code>Queue</code> 를 구현하면 다음과 같다.</p>
<pre><code class="language-swift">public struct Queue&lt;T&gt; {
    fileprivate var array = [T?]()
    fileprivate var head = 0

    public var isEmpty: Bool {
        return count == 0
    }

    public var count: Int {
        return array.count - head
    }

    public mutating func enqueue(_ element: T) {
        array.append(element)
    }

    public mutating func dequeue() -&gt; T? {
        guard head &lt; array.count, let element = array[head] else { return nil }

        array[head] = nil
        head += 1

        let percentage = Double(head) / Double(array.count)
        if array.count &gt; 50 &amp;&amp; percentage &gt; 0.25 {
            array.removeFirst(head)
            head = 0
        }

        return element
    }

    public var front: T? {
        if isEmpty {
            return nil
        } else {
            return array[head]
        }
    }
}</code></pre>
<p>배열의 요소를 빈 채로 놔둬야 하기 때문에 이 때의 배열은 <code>T</code> 형이 아닌 <code>T?</code> 형 객체를 담게 된다. 변수 <code>head</code> 는 배열의 가장 앞 요소의 인덱스를 담는다.</p>
<p>대부분의 새로운 기능들은 <code>dequeue()</code> 에 있다. 요소를 큐에서 제거할 때(dequeue) 첫 번째로 할 일은 배열에서 해당 객체를 없애기 위해 <code>array[head]</code> 를 <code>nil</code> 로 만드는 것이다. 그다음 <code>head</code> 를 증가시켜야 하는데, 앞의 요소가 삭제됨에 따라 뒤의 요소가 첫 번째 요소가 되기 때문이다.</p>
<p>아래와 같은 상태에서,</p>
<pre><code class="language-swift">[ &quot;Ada&quot;, &quot;Steve&quot;, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx ]
  head</code></pre>
<p>다음과 같은 상태로 바뀌는 것이다.</p>
<pre><code class="language-swift">[ xxx, &quot;Steve&quot;, &quot;Tim&quot;, &quot;Grace&quot;, xxx, xxx ]
        head</code></pre>
<p>이는 마치 슈퍼마켓에서 사람들이 계산대로 한칸씩 당겨져 오는 것이 아니라, 계산대가 한칸씩 큐를 타고 올라가는 것과 같다.</p>
<p>만약 앞의 빈 공간을 없애지 않으면 큐에 추가하고 삭제하는(enqueue, dequeue) 과정에서 배열의 크기는 계속 늘어날 것이다. 주기적으로 배열을 잘라내기 위해 다음과 같이 해야 한다.</p>
<pre><code class="language-swift">let percentage = Double(head)/Double(array.count)
    if array.count &gt; 50 &amp;&amp; percentage &gt; 0.25 {
      array.removeFirst(head)
      head = 0
    }</code></pre>
<p>이는 전체 배열의 크기에서 빈 공간이 차지하는 비율을 퍼센트로 계산하는 부분이다. 배열의 25% 이상이 쓰이지 않으면, 낭비되는 그 공간을 잘라내버린다. 하지만 배열의 크기가 낭비되는 공간을 무시할만큼 작다면 크기 조정을 하지 않아도 되므로, 배열에 적어도 50개의 요소가 있어야만 배열을 잘라내도록 했다.</p>
<blockquote>
<p>주의 : 위의 숫자는 순식간에 적어낸 것이다. 큐를 사용할 앱이나 환경에 따라서 이를 수정해야 할 수도 있다.</p>
</blockquote>
<p>이를 플레이그라운드 상에서 테스트하고자 한다면 다음과 같이 하면 된다.</p>
<pre><code class="language-swift">var q = Queue&lt;String&gt;()
q.array                   // [] 비어있는 배열

q.enqueue(&quot;Ada&quot;)
q.enqueue(&quot;Steve&quot;)
q.enqueue(&quot;Tim&quot;)
q.array             // [{Some &quot;Ada&quot;}, {Some &quot;Steve&quot;}, {Some &quot;Tim&quot;}]
q.count             // 3

q.dequeue()         // &quot;Ada&quot;
q.array             // [nil, {Some &quot;Steve&quot;}, {Some &quot;Tim&quot;}]
q.count             // 2

q.dequeue()         // &quot;Steve&quot;
q.array             // [nil, nil, {Some &quot;Tim&quot;}]
q.count             // 1

q.enqueue(&quot;Grace&quot;)
q.array             // [nil, nil, {Some &quot;Tim&quot;}, {Some &quot;Grace&quot;}]
q.count             // 2</code></pre>
<p>여기서 잘라내는 행동을 테스트해보고 싶다면,</p>
<pre><code class="language-swift">if array.count &gt; 50 &amp;&amp; percentage &gt; 0.25 {</code></pre>
<p>이 부분을 다음과 같이 바꾸면 된다.</p>
<pre><code class="language-swift">if head &gt; 2 {</code></pre>
<p>이제 다른 요소를 큐에서 제거(dequeue)하면 배열은 다음과 같아질 것이다.</p>
<pre><code class="language-swift">q.dequeue()         // &quot;Tim&quot;
q.array             // [{Some &quot;Grace&quot;}]
q.count             // 1</code></pre>
<p>앞 부분의 <code>nil</code> 부분은 제거되고, 더이상 배열에 낭비되는 공간은 없다. 새로운 버전의 <code>Queue</code> 는 첫번째 것보다 크게 복잡하지 않으면서 큐에서 제거하는 것(dequeue) 또한 O(1) 만큼의 시간을 갖는다. 이는 우리가 배열을 어떻게 쓰는지에 대해 궁리한 결과이다.</p>
<h2 id="참고하기">참고하기</h2>
<hr>
<p>큐를 만드는 방법은 정말 많다. 대안으로 <strong>연결리스트(linked list), 원형 버퍼(circular buffer)</strong> 또는 <strong>힙</strong>이 있다.</p>
<p>이를 변형한 것에는 큐에 추가와 제거(enqueue, dequeue)를 양 끝에서 할 수 있는 <strong>덱(deque, double-ended queue)</strong>과, 가장 중요한 요소가 앞에 배치되는 정렬된 큐인 <strong>우선순위 큐(priority queue)</strong>가 있다.</p>
<p><em>Written for Swift Algorithm Club by Matthijs Hollemans</em>
<em>원문 <a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Queue">https://github.com/raywenderlich/swift-algorithm-club/tree/master/Queue</a></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift | 스택, Swift Algorithm Club 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%EC%8A%A4%ED%83%9D-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%EC%8A%A4%ED%83%9D-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Tue, 13 Jul 2021 01:53:38 GMT</pubDate>
            <description><![CDATA[<p><em>✔︎ 이 글은 <a href="https://github.com/raywenderlich/swift-algorithm-club">Swift Algorighm Club</a> 의 문서를 번역한 글입니다.</em></p>
<h2 id="스택">스택</h2>
<hr>
<p>스택은 한정된 기능을 갖는 배열과 같다. 스택의 맨 꼭대기에 새로운 요소를 추가하기 위해서 <em><code>푸시(push)</code></em> 를, 꼭대기부터 요소를 제거하기 위해서 <em><code>팝(pop)</code>*을, 꼭대기의 요소를 제거하지 않고 확인하기 위해서 *<code>피크(peek)</code></em> 를 한다.</p>
<p>이게 왜 필요할까? 많은 알고리즘에서 당신은 어느 순간, 일시적인 리스트에 값을 추가하고 싶을 것이고, 나중에 그 값을 리스트로부터 꺼내오고 싶을 것이다. 때때로 이 값들을 추가하고 제거하는 순서가 중요하다.</p>
<p>스택은 <strong>후입선출(LIFO-Last In First Out)</strong>의 순서로 구성된다. 마지막에 푸시한 요소가 다음 팝의 첫 번째 요소가 된다. (이와 아주 비슷한 자료구조가 있는데, 선입선출-FIFO-First In First Out의 큐가 그 것이다.)</p>
<h2 id="예시">예시</h2>
<hr>
<p>예를 들어, 스택에 수를 푸시해보자.</p>
<pre><code class="language-jsx">stack.push(10)</code></pre>
<p>스택은 <code>[ 10 ]</code> 이 된다. 하나 더 푸시해보면,</p>
<pre><code class="language-jsx">stack.push(3)</code></pre>
<p>스택은 <code>[ 10, 3 ]</code> 이 된다. 또 하나 푸시해보면,</p>
<pre><code class="language-jsx">stack.push(57)</code></pre>
<p>스택은 <code>[ 10, 3, 57 ]</code> 이 된다. 여기서 꼭대기 숫자를 스택에서 팝 해보자.</p>
<pre><code class="language-jsx">stack.pop()</code></pre>
<p>이는 <code>57</code> 을 반환하는데, 우리가 가장 최근에 푸시한 값이 57이기 때문이다. 스택은 다시 <code>[ 10, 3 ]</code> 이 된다.</p>
<pre><code class="language-jsx">stack.pop()</code></pre>
<p>이번엔 <code>3</code> 을 반환하고, 쭉 같은 방식으로 계속된다. 스택이 비어있을 때 팝을 한다면 <code>nil</code> 을 반환하거나, 어떤 구현 코드에서는 &quot;스택 언더플로우(stack underflow)&quot; 와 같은 오류 문자를 출력할 것이다.</p>
<h2 id="구현">구현</h2>
<hr>
<p>스위프트로 스택을 만드는 건 쉽다. 푸시 하고, 팝 하고, 맨 꼭대기 요소를 볼 수 있게 하는 배열을 보기 좋게 만든 것 뿐이다.</p>
<blockquote>
<p><em>(원문엔 stack 이 wrapper 라고 한다. 프로그래밍에서 이 단어를 어떻게 쓰는지 검색했을 때, 일반적으로 다른 클래스의 인스턴스를 가지는 클래스를 말할 때 쓴다고 한다. 래퍼의 주요 목적은 객체를 다른 방식으로 제공하는 것이기 때문이다. 관련 링크 : <a href="https://stackoverflow.com/questions/3293752/where-and-how-is-the-term-used-wrapper-in-programming-what-does-it-help-to-do">https://stackoverflow.com/questions/3293752/where-and-how-is-the-term-used-wrapper-in-programming-what-does-it-help-to-do</a>)</em></p>
</blockquote>
<pre><code class="language-swift">public struct Stack&lt;T&gt; {
    fileprivate var array = [T]()

    public var isEmpty: Bool {
        return array.isEmpty
    }

    public var count: Int {
        return array.count
    }

    public mutating func push(_ element: T) {
        return array.append(element)
    }

    public mutating func pop() -&gt; T? {
        return array.popLast()
    }

    public var top: T? {
        return array.last
    }
}</code></pre>
<p>여기서 주목할 점은 푸시를 통해 새로운 요소가 배열의 처음이 아닌 <strong>끝에 추가된다</strong>는 것이다. 배열의 처음에 추가하는 것은 배열에 존재하는 모든 요소들을 메모리상에서 옮겨줘야 하기 때문에 <strong>O(n)</strong> 만큼의 비용이 든다. 마지막에 추가하는 것은 배열의 크기와 관계 없이 언제나 같은 시간이 걸리므로 <strong>O(1)</strong> 만큼이 든다.</p>
<p>스택에 대한 재밌는 사실 : 당신이 함수나 메소드를 호출할 때마다 CPU 는 스택이라는 공간에 반환 주소를 저장한다. 함수가 끝났을 때 CPU가 그 반환 주소를 사용해 호출자에게 다시 돌아간다. 끝나지 않는 재귀함수와 같이 엄청나게 많은 함수들을 호출했을 때 CPU 스택에 공간이 없기 때문에 &quot;스택 오버플로우&quot;라 불리는 현상을 보게 된다.</p>
<hr>
<p><em>Written for Swift Algorithm Club by Matthijs Hollemans</em></p>
<p><em>원문 : <a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Stack">https://github.com/raywenderlich/swift-algorithm-club/tree/master/Stack</a></em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift | 이진 검색, Swift Algorithm Club 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%EC%9D%B4%EC%A7%84-%EA%B2%80%EC%83%89-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%EC%9D%B4%EC%A7%84-%EA%B2%80%EC%83%89-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Mon, 12 Jul 2021 07:47:55 GMT</pubDate>
            <description><![CDATA[<p><em>✔︎ 이 글은 <a href="https://github.com/raywenderlich/swift-algorithm-club">Swift Algorithm Club</a> 문서의 번역본입니다.</em></p>
<h2 id="이진-검색">이진 검색</h2>
<hr>
<blockquote>
<p>목적 : 배열에서 요소를 빠르게 찾기.</p>
</blockquote>
<p>숫자 배열이 주어지고, 이 중 특정한 숫자가 배열에 있는지, 있다면 몇 번째 인덱스에 있는지 알고싶다고 가정하자.</p>
<p>대부분의 경우에, 스위프트의 <code>Collection.index(of:)</code> 함수로 충분할 것이다.</p>
<pre><code class="language-swift">let numbers = [11, 59, 3, 2, 53, 17, 31, 7, 19, 67, 47, 13, 37, 61, 29, 43, 5, 41, 23]

numbers.index(of:43) // returns 15</code></pre>
<p><code>Collection.index(of:)</code> 내장 함수는 <strong><a href="https://velog.io/@seri_ous/Swift-%EC%84%A0%ED%98%95-%EA%B2%80%EC%83%89-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD">선형 검색(linear search)</a></strong> 으로 진행된다. 코드는 다음과 같이 생겼다.</p>
<pre><code class="language-swift">func linearSearch&lt;T: Equatable&gt;(_ a: [T], _ key: T) -&gt; Int? {
    for i in 0..&lt;a.count {
        if a[i] == key {
            return i
        }
    }
    return nil
}</code></pre>
<p>그리고 이 함수는 이렇게 쓰면 된다.</p>
<pre><code class="language-swift">linearSearch(numbers, 43) // returns 15</code></pre>
<p>뭐가 문젤까? <code>linearSearch()</code> 함수는 당신이 찾고자 하는 요소를 찾을 때까지 배열의 전체를 처음부터 끝까지 돈다. 최악의 경우엔 값이 배열에 없어 지금까지의 행동이 무의미하게 끝나버린다.</p>
<p>평균적으로 선형 검색 알고리즘은 배열의 절반을 확인해야 한다. 배열이 크다면, 속도가 굉장히 느려질 것이다.</p>
<h2 id="분할-정복">분할 정복</h2>
<hr>
<p>위 방식에 속도를 더하는 방식은 <em>이진 탐색(binary search)</em>이다. 비법은 값을 찾을 때까지 배열을 반으로 쪼개는 데 있다.</p>
<p>크기가 <code>n</code> 인 배열에서 선형 검색이 <strong>O(n)</strong> 의 시간복잡도를 갖는 반면, 이진 검색은 <strong>O(log n)</strong> 뿐이 걸리지 않는다. 예를 들어, 요소의 개수가 1,000,000인 배열에서 단 20번만(<code>log_2(1,000,000) = 19.9</code>) 탐색하면 원하는 값을 찾을 수 있다. 요소의 개수가 10억개인 경우에도 단 30번만 거치면 찾을 수 있다. (하긴, 10억 개의 요소를 가진 배열을 언제 마지막으로 썼겠냐만..)</p>
<p>보기엔 좋아보이지만 이진 탐색엔 배열이 <strong>정렬 돼 있어야 한다는</strong> 단점이 있다. 물론, 실제론 문제가 되진 않는다.</p>
<p>이진 검색은 이렇게 동작한다 :</p>
<ul>
<li>배열을 반으로 쪼갠 뒤 당신이 찾는 <em>키값</em>이 절반의 왼쪽 편에 있는지, 오른쪽 편에 있는지 판별한다.</li>
<li>키값이 어디에 있는지 어떻게 아는가? 여기서 배열을 정렬해야 하는 이유가 나온다. 정렬된 배열에서 단순히 <code>&lt;</code> 나 <code>&gt;</code> 연산자로 비교하면 된다.</li>
<li>키값이 왼쪽 절반에 있다면, 거기서 똑같은 동작을 반복하면 된다 : 왼쪽 절반을 반으로 쪼개 더 작은 조각으로 만든 뒤 키값이 어느 범위에 있는지 판별한다. (오른쪽 절반에 대해서도 똑같이 적용한다.)</li>
<li>키값을 찾을 때까지 반복한다. 더이상 배열을 쪼갤 수 없으면, 배열에 키값이 없다고 결론짓는다.</li>
</ul>
<p>왜 &quot;이진&quot; 검색이라는 지 알 수 있다. 매 단계마다 배열을 반으로 쪼개기 때문이다. 이 <em>분할정복</em> 과정은 찾는 키값이 어디에 있는지 빠르게 좁혀나갈 수 있다.</p>
<h2 id="코드">코드</h2>
<hr>
<p>재귀를 활용해 짜여진 이진 검색 코드는 다음과 같다.</p>
<pre><code class="language-swift">func binarySearch&lt;T: Comparable&gt;(_ a: [T], key: T, range: Range&lt;Int&gt;) -&gt; Int? {
    if range.lowerBound &gt;= range.upperBound {
        // 찾는 값이 배열에 없음
        return nil

    } else {
            // 배열을 자를 기준 계산
            let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2

            // 찾는 값이 왼편에 있을 때
            if a[midIndex] &gt; key {
                return binarySearch(a, key: key, range: range.lowerBound..&lt;midIndex)

            // 찾는 값이 오른편에 있을 때
            } else if a[midIndex] &lt; key {
                return binarySearch(a, key: key, range: midIndex + 1..&lt;range.upperBound)

            // 찾는 값 발견!
            } else {
                return midIndex
            }
     }
}</code></pre>
<p>플레이그라운드에 위 코드를 복사한 뒤 아래와 같이 실행해볼 수 있다 :</p>
<pre><code class="language-swift">let numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67]

binarySearch(numbers, key: 43, range: 0 ..&lt; numbers.count)  // gives 13</code></pre>
<p><code>numbers</code> 배열이 정렬되어 있는 것에 주목하자. 그렇지 않으면 이진 검색 알고리즘은 제대로 동작하지 않을 것이다.</p>
<p>이진 검색은 배열을 절반으로 쪼개면서 진행된다고 했지만, 새로운 두 개의 배열을 실제로 만들지는 않는다. 스위프트의 <code>range</code> 객체를 사용해 쪼개진 상태를 추적해간다. 초기엔, 이 범위가 <code>0..&lt;numbers.count</code> 와 같이 전체를 감당한다. 우리가 배열을 쪼개면 쪼갤수록 이 범위는 점차 작아진다.</p>
<blockquote>
<p><strong>주의</strong> : <code>range.upperBound</code> 는 언제나 마지막 요소 + 1을 가리킨다는 점을 주의해야 한다. 예를 들어, 배열에 19개의 요소가 있는 배열의 범위가 <code>0..&lt;19</code> 일 때, <code>range.lowerBound = 0</code> 이고, <code>range.upperBound = 19</code> 가 된다. 하지만 우리는 배열의 인덱스를 0부터 세기 때문에 마지막 요소의 인덱스는 19가 아닌 18이다. 범위와 함께 작업할 때 이를 꼭 기억하자 : <code>upperBound</code> 는 항상 마지막 요소의 인덱스보다 하나 더 많다는 점을.</p>
</blockquote>
<h2 id="예시를-단계별로-따라가보자">예시를 단계별로 따라가보자</h2>
<hr>
<p>구체적으로 알고리즘이 어떻게 동작하는지 보면 도움이 될 것이다.</p>
<p>위의 예시에서 배열은 19개의 숫자를 갖고, 정렬된 상태에서 아래와 같은 모습을 보인다.</p>
<pre><code class="language-swift">[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 ]</code></pre>
<p><code>43</code> 이 해당 배열의 어디에 위치하는지를 아는 것이 목적이다.</p>
<p>배열을 반으로 쪼개기 위해서, 우리는 배열의 중간에 위치한 요소의 인덱스를 알아야 한다.</p>
<pre><code class="language-swift">let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2</code></pre>
<p>처음엔, 범위에서 <code>lowerBound = 0</code> 이고, <code>upperBound = 19</code> 이다. <code>midIndex</code> 가 <code>0 + (19 - 0) / 2 = 9</code> 라는 것을 알 수 있다. 사실 9.5지만, 우리는 정수를 사용하기 때문에 값을 내림한다.</p>
<p>다음 순서부터, <code>*</code> 표시는 가운데 요소를 보여 준다. 아래서 확인할 수 있듯, 각 부분에 있는 숫자의 개수는 동일하며, 우리는 정 가운데를 기준으로 배열을 쪼갤 수 있다.</p>
<pre><code class="language-swift">[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 ]
                                  *</code></pre>
<p>이제 이진 탐색으로 어떤 절반을 사용할 지 판별한다. 관련된 코드 조각은 다음과 같다.</p>
<pre><code class="language-swift">if a[midIndex] &gt; key {
    // 왼쪽 절반을 사용
} else if a[midIndex] &lt; key {
    // 오른쪽 절반을 사용
} else {
    return midIndex
}</code></pre>
<p>이 경우에, <code>a[midIndex] = 29</code> 이다. 이는 찾고자 하는 값보다 작기 때문에 우리는 찾고자 하는 값이 절대 왼쪽 편에 위치하지 않으리라 결론내릴 수 있다. 어쨌든 왼쪽 편은 29보다 작은 수들로 구성돼있기 때문이다. 따라서 찾는 값은 무조건 오른쪽 편에 있을 것(혹은 배열에 없거나)이다.</p>
<p>이제 우리는 이진 검색을 반복하면 되는데, <code>midIndex + 1</code> 부터 <code>range.upperBound</code> 까지의 배열에서만 하면 된다.</p>
<pre><code class="language-swift">[ x, x, x, x, x, x, x, x, x, x | 31, 37, 41, 43, 47, 53, 59, 61, 67 ]</code></pre>
<p>왼쪽 편에 대해서는 신경 쓸 필요가 없기 때문에 <code>x</code> 로 표시했다. 지금부터는 인덱스가 10으로 시작하는 오른쪽 편만 보면 된다.</p>
<p>새로운 중간 값을 계산하면, <code>midIndex = 10 + (19 - 10)/2 = 14</code> 가 되고, 이를 이용해 반으로 쪼개면 다음과 같아진다.</p>
<pre><code class="language-swift">[ x, x, x, x, x, x, x, x, x, x | 31, 37, 41, 43, 47, 53, 59, 61, 67 ]
                                                 *</code></pre>
<p>확인할 수 있듯, <code>a[14]</code> 의 값이 오른편의 중간값이 된다.</p>
<p>찾고 있는 값이 <code>a[14]</code> 보다 큰가 작은가? <code>43 &lt; 47</code> 이기 때문에 작다. 이번엔 왼쪽편을 보고, 오른쪽 편에 대해선 무시하도록 하자.</p>
<pre><code class="language-swift">[ x, x, x, x, x, x, x, x, x, x | 31, 37, 41, 43 | x, x, x, x, x ]</code></pre>
<p>이제 새로운 <code>midIndex</code> 는 다음과 같다.</p>
<pre><code class="language-swift">[ x, x, x, x, x, x, x, x, x, x | 31, 37, 41, 43 | x, x, x, x, x ]
                                     *</code></pre>
<p>찾는 값이 <code>37</code> 보다 크므로, 오른쪽 편으로 반복한다.</p>
<pre><code class="language-swift">[ x, x, x, x, x, x, x, x, x, x | x, x | 41, 43 | x, x, x, x, x ]
                                        *</code></pre>
<p>다시, 찾는 값이 더 크므로 다시 한 번 쪼갠 뒤 오른쪽 편을 취한다.</p>
<pre><code class="language-swift">[ x, x, x, x, x, x, x, x, x, x | x, x | x | 43 | x, x, x, x, x ]
                                            *</code></pre>
<p>이제 끝났다. 찾는 값이 배열의 요소와 같아졌으므로, 우리가 찾고 있는 값 <code>43</code> 이 배열의 <code>13</code> 인덱스에 있음을 찾았다. 오예!</p>
<p>많은 일을 한 것 같지만, 실은 배열에서 키값을 찾기 위해 4번의 과정밖에 거치지 않았고, 이는 <code>log_2(19) = 4.23</code> 이라는 값으로도 확인할 수 있다. 선형 검색으로는 14번의 과정을 밟아야 했을 것이다.</p>
<p>우리가 <code>43</code> 대신 <code>42</code> 를 검색해야 했다면 어떤 일이 일어날까? 이 상황에서, 우리는 배열을 더이상 쪼갤 수 없다. <code>range.upperBound</code> 가 <code>range.lowerBound</code> 보다 작아진다. 이는 키값이 배열에 없다는 것을 의미하며, <code>nil</code> 을 반환한다.</p>
<blockquote>
<p><strong>주의</strong>: 이진 검색을 구현할 때 많이들 <code>midIndex = (lowerBound + upperBound) / 2</code> 로 계산한다. 이는 사소한 버그를 일으킬 수 있는데, 배열의 크기가 엄청 커서 <code>lowerBound + upperBound</code> 가 정수의 최댓값을 넘어서는 오버플로우를 일으킬 때 발생한다. 이는 64비트 CPU 에서는 잘 일어나지 않지만, 32비트 환경에서는 분명히 일어날 수 있다.</p>
</blockquote>
<h2 id="반복-vs-재귀">반복 vs 재귀</h2>
<hr>
<p>이진 검색은 같은 로직을 더 작은 서브 배열에 계속 적용하기 때문에 보통 재귀의 형태를 가진다. 하지만, 꼭  <code>binarySearch()</code> 함수가 재귀 함수여야 하는 것은 아니다. 어떨 땐 재귀 알고리즘을 단순한 루프로 바꾸는 것이 더 효과적이다.</p>
<p>이진 검색의 반복 구현은 다음과 같다.</p>
<pre><code class="language-swift">func binarySearch&lt;T: Comparable&gt;(_ a: [T], key: T) -&gt; Int? {
    var lowerBound = 0
    var upperBound = a.count
    while lowerBound &lt; upperBound {
        let midIndex = lowerBound + (upperBound - lowerBound) / 2
        if a[midIndex] == key {
            return midIndex
        } else if a[midIndex] &lt; key {
            lowerBound = midIndex + 1
        } else {
            upperBound = midIndex
        }
    }
    return nil
}</code></pre>
<p>확인할 수 있듯, 재귀함수로 구현된 것과 아주 유사하다. 가장 큰 차이점은 <code>while</code> 반복문을 사용한다는 점이다.</p>
<p>이와 같이 사용할 수 있다.</p>
<pre><code class="language-swift">let numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67]

binarySearch(numbers, key: 43)  // gives 13</code></pre>
<h2 id="결론">결론</h2>
<hr>
<p>배열이 먼저 정렬돼야 한다는 점이 문제일까? 경우에 따라 다르다. 정렬하는 것 - 이진 검색을 곁들인 것 - 은 단순한 선형 검색보다 더 느릴 수 있음을 기억하자. 이진 탐색은 딱 한 번 정렬하고, 많은 검색을 할 때 최고로 좋은 방법이다.</p>
<p><a href="https://en.wikipedia.org/wiki/Binary_search_algorithm">위키피디아</a> 참고!
<em>Written for Swift Algorithm Club by Matthijs Hollemans</em>
원문
<a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Binary%20Search">https://github.com/raywenderlich/swift-algorithm-club/tree/master/Binary%20Search</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift | 선형 검색, Swift Algorithm Club 번역]]></title>
            <link>https://velog.io/@seri_ous/Swift-%EC%84%A0%ED%98%95-%EA%B2%80%EC%83%89-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</link>
            <guid>https://velog.io/@seri_ous/Swift-%EC%84%A0%ED%98%95-%EA%B2%80%EC%83%89-Swift-Algorithm-Club-%EB%B2%88%EC%97%AD</guid>
            <pubDate>Mon, 12 Jul 2021 07:42:12 GMT</pubDate>
            <description><![CDATA[<p>✔︎ <em>이 글은 <a href="https://github.com/raywenderlich/swift-algorithm-club">Swift Algorithm Club</a> 문서의 번역본입니다.</em></p>
<h2 id="선형-검색">선형 검색</h2>
<hr>
<p>목표 : 배열에서 특정한 값을 찾기.</p>
<p>제네릭 배열이 있다고 가정하자. 선형 검색을 통해서 우리는 배열을 하나씩 하나씩 방문해 우리가 찾는 값을 배열의 각 요소와 비교한다. 두 개가 같으면 멈추고 그 때 배열의 인덱스를 반환한다. 아니면, 배열의 모든 값을 다 볼 때까지 다음 값과의 비교를 반복한다.</p>
<h2 id="예시">예시</h2>
<hr>
<p><code>[5, 2, 4, 7]</code> 로 구성된 배열이 있고, 이 배열이 <code>2</code> 를 포함하고 있는지 확인해보자.</p>
<p>우리는 배열의 첫 번째 요소인 <code>5</code> 로 우리가 찾는 값 <code>2</code> 와의 비교를 시작한다. 두 값은 명백히 다른 값이므로 배열의 다음 요소와의 비교를 시행한다.</p>
<p>다음 요소인 <code>2</code> 와 우리가 찾는 값 <code>2</code> 를 비교했을 때, 두 값은 같다. 우리는 반복을 그만하고 <code>2</code> 가 위치한 배열의 인덱스인 1을 반환한다.</p>
<h2 id="코드">코드</h2>
<hr>
<p>선형 검색을 스위프트로 구현하면 다음과 같다 :</p>
<pre><code class="language-swift">func linearSearch&lt;T: Equatalbe&gt;(_ array: [T], _ object: T) -&gt; Int? {
    for (index, obj) in array.enumerated() where obj == object {
        return index
    }
    return nil
}</code></pre>
<p>플레이그라운드 상에서 다음과 같이 테스트해보자 :</p>
<pre><code class="language-swift">let array = [5, 2, 4, 7]
linearSearch(array, 2)     // This will return 1</code></pre>
<h2 id="성능">성능</h2>
<hr>
<p>선형 검색의 시간복잡도는 <strong>O(n)</strong> 이다. 우리가 찾는 값을 배열의 각 요소와 비교하기 때문에, 소요되는 시간은 배열의 길이와 비례한다. 최악의 경우, 우리는 배열의 모든 요소를 봐야 할 수도 있다.</p>
<p>최선의 경우엔 <strong>O(1)</strong> 의 시간복잡도를 갖는데, 이는 우리가 찾는 값이 배열의 맨 처음에 위치해 탐색을 시작하자마자 발견돼야 하기 때문에 흔치 않다. 운 좋을 수도 있지만, 대부분의 경우 그렇지 않다. 평균적으로, 선형 검색은 배열의 절반에 해당하는 값들을 봐야 한다.</p>
<h2 id="참고">참고</h2>
<hr>
<p><a href="https://en.wikipedia.org/wiki/Linear_search">선형 검색 위키피디아</a>
<em>Written by Patrick Balestra</em></p>
<p>원문
<a href="https://github.com/raywenderlich/swift-algorithm-club/tree/master/Linear%20Search">https://github.com/raywenderlich/swift-algorithm-club/tree/master/Linear%20Search</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[42서울 라피신을 앞두고 설렘과 두려움을 구글링을 통해 다잡으려는 예비 피시너들(특히 비전공자)에게]]></title>
            <link>https://velog.io/@seri_ous/42%EC%84%9C%EC%9A%B8-%EB%9D%BC%ED%94%BC%EC%8B%A0%EC%9D%84-%EC%95%9E%EB%91%90%EA%B3%A0-%EC%84%A4%EB%A0%98%EA%B3%BC-%EB%91%90%EB%A0%A4%EC%9B%80%EC%9D%84-%EA%B5%AC%EA%B8%80%EB%A7%81%EC%9D%84-%ED%86%B5%ED%95%B4-%EB%8B%A4%EC%9E%A1%EC%9C%BC%EB%A0%A4%EB%8A%94-%EC%98%88%EB%B9%84-%ED%94%BC%EC%8B%9C%EB%84%88%EB%93%A4%ED%8A%B9%ED%9E%88-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%97%90%EA%B2%8C</link>
            <guid>https://velog.io/@seri_ous/42%EC%84%9C%EC%9A%B8-%EB%9D%BC%ED%94%BC%EC%8B%A0%EC%9D%84-%EC%95%9E%EB%91%90%EA%B3%A0-%EC%84%A4%EB%A0%98%EA%B3%BC-%EB%91%90%EB%A0%A4%EC%9B%80%EC%9D%84-%EA%B5%AC%EA%B8%80%EB%A7%81%EC%9D%84-%ED%86%B5%ED%95%B4-%EB%8B%A4%EC%9E%A1%EC%9C%BC%EB%A0%A4%EB%8A%94-%EC%98%88%EB%B9%84-%ED%94%BC%EC%8B%9C%EB%84%88%EB%93%A4%ED%8A%B9%ED%9E%88-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%97%90%EA%B2%8C</guid>
            <pubDate>Mon, 26 Apr 2021 16:25:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>철이 없었죠. 코딩이 좋아서 공부를 시작했다는 자체가</em></p>
</blockquote>
<h3 id="예피하">예피<del>하</del>!</h3>
<p>안녕하세요, 저는 42 서울 4기 2차 라 피신을 막 끝내고 본과정을 앞두고 있는 찐 비전공자 예비 카뎃입니다.</p>
<p>피신 시작할 땐 매일 일기처럼 써야지~ 했는데 역시나 사람은 쉽게 변하지 않네요. 끝난 지 열흘이 다 돼 가는 이 시점에서 흐려져가는 기억을 더듬어봅니다.</p>
<p>저 또한 인터넷에 흩어져 있는 수많은 글을 찾아보면서 준비를 했기 때문에 (물론 가서 멘붕오는 건 피할 수 없음), 떨리는 마음을 진정시킬 수 있는 방법이 될 수 있길 바라며 글을 씁니다!</p>
<hr>
<h3 id="seri는요--">seri는요. . .</h3>
<p><em><del>글쓴이의 나이대가 느껴지는,,</del></em>
<img src="https://images.velog.io/images/seri_ous/post/abe74ee2-130e-4e0c-accd-5020bbc9e7ca/%E3%85%87%E3%85%87.jpeg" alt=""></p>
<p><em>찐 비전공자예요... .. 코딩의 ㅋ자도 모르던. . .
이과 없는 문과 고등학교에서.. . . 예체능쪽으로 대학을 갔구요.. ..
컴퓨터 공부 한다고 하면. .. 친구들이 물어요 .. . &quot;뭐 코딩 그런거 하는거야?&quot;
그 이상 대화가 이어지지 않아요 . .. ..</em></p>
<p>저는 원래 영상을 하던 사람이었습니다. 방송국 취업 하려고 인턴까지 했는데 계속 &#39;이게 맞나?&#39; 하는 의문이 들었어요. 수직적인 조직 문화가 힘들었을 뿐만 아니라, 하고 있는 일이 제 가치관과 맞지 않다고 느꼈습니다.</p>
<p>일에선 즐거움을 느낄 수 없었고, 스트레스를 해소하기 위해 다이어리 어플을 다운받았습니다.
신나게 상사 욕과 우울한 마음을 뱉어대던 어느 날, &#39;어? 근데 이거 나도 만들겠는데?&#39; 라는 생각이 들었어요. 다음 날부터 아이패드에 있는 &#39;swift playground(스크래치같은 swift 교육 앱)&#39;를 틈틈히 했고, 그 과정에서 공부를 해보고 싶다는 마음이 생겼습니다.</p>
<p>엄청난 스트레스를 받음에도 불구하고, 대학 내내 쌓아온 소위 &#39;스펙&#39;이 아까워서, 그리고 새로운 분야에 대한 도전이 두려워서 얼마 동안은 해 오던 걸 버리지 못했어요. 놓지도 잡지도 못하는 상태로 질질 끌다가 마음을 다잡고 공부를 시작했습니다.</p>
<p>&#39;나는 지식도, 정보도, 인맥도 없는 찐 비전공자니 내게 가르침을 주고 함께 아이디어를 실현해나갈 사람들이 필요해...&#39;</p>
<p>라는 생각을 시작으로 교육 프로그램 검색을 하다가 42 서울을 알게 됐고, C 언어로 과정이 진행된다는 것을 보고 당장 중고서점에 달려가 윤성우의 열혈 C 책을 사서 키보드를 두드렸습니다.</p>
<p>한 달 정도 되는 시간 동안 매일 공부했고, 그 배움의 과정이 너무나도 짜릿했습니다. 기본적인 수준이지만 컴퓨터 언어를 알고 코드를 짤 수 있다는 것 자체가 자신감을 북돋아 주었던 것 같습니다.</p>
<p>예전엔 전혀 알지 못했던 분야에 도전하는 일은 어렵지만, 영 못 할 일은 아니라는 생각이 듭니다. <strong>정말 아무 베이스가 없는 진또배기 비전공자</strong>(그 흔한 경제수업도 안들어봄)인데 어떡하지, 싶은 분들도 관심과 열의가 있다면 하실 수 있습니다. 물론, 심각한 컴맹 혹은 진짜 노관심이라면 어렵겠지만요.</p>
<hr>
<h3 id="라피신-전에-준비한-것">라피신 전에 준비한 것</h3>
<p>앞에 구구절절 이야기했듯, 저는 <strong>C 언어를 미리 공부</strong>했습니다.
제가 마음을 먹은 당시(1월) 가장 빨리 시작할 수 있는 코딩 부트캠프가 42서울이었고, 그래서 저는 42서울을 목표로 무작정 C 언어를 공부했습니다.
덕분에 C 언어 공부하기가 2021년 제가 가장 잘 한 일 현재 TOP 1에 위치하고 있습니다. 미리 공부를 한 덕에 피신 기간 동안 멘탈이 터질 일이 별로 없었기 때문입니다.</p>
<p>라피신 후기를 찾아보면 사람들이 맥 os를 비롯한 아이맥 사용법에 1차 멘붕, 쉘 스크립트에 2차 멘붕이 온 것 같았습니다. 다행히(?) 영상 관련 일을 하느라 예전부터 맥북을 썼던 터라 맥 os는 큰 문제가 아니었지만, 쉘은 문제였습니다.</p>
<p>강의는 재미도 없고, 명령어들을 암기해야 할 것 같은 부담이 생겨 연습 겸으로 필요한 기능을 터미널 명령어들을 활용해 설치했습니다. 맥 os를 세 번 다시 까는 개고생 끝에 화질구지 사이드카를 얻고 터미널과 구면이 되었습니다.</p>
<p>이 시도 이후 저는 터미널 명령어를 몰라도, 그냥 <strong>구글링 열심히</strong> 하면 뭐든 된다는 자신감을 얻었습니다. 이 자신감 덕분에 멘탈 터질 위기의 1주차를 잘 버텨냈다고 생각합니다.</p>
<h3 id="라피신에-전에-검색한-것">라피신에 전에 검색한 것</h3>
<p><img src="https://images.velog.io/images/seri_ous/post/838be155-4576-4bd6-99ff-5a9cd73393e1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-27%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.54.47.png" alt="">
주변에 코딩의 ㅋ자도 모르는 지인들밖에 없는 탓에 저는 인터넷 정보에 의존해야 했습니다. 꽤나 많은 글을 찾아 보고 라피신에 대해 찾아낸 정보는 다음과 같았습니다.</p>
<ol>
<li>교수, 선생 등이 없음</li>
<li>팀플이 있음</li>
<li>시험이 있음</li>
<li>돈을 줌</li>
<li>한 달 동안 죽어라 해야됨</li>
<li>본과정 합격에 관한 뇌피셜들</li>
</ol>
<p>사람들이 가장 궁금해할 것들이며, 꽤나 많은 정보가 나와 있어 저도 마음 편하게 후기나 팁을 남길 수 있는 부분인 것 같아 이야기를 해 보려 합니다.</p>
<h4 id="1-교수-선생-등이-없음">1. 교수, 선생 등이 없음</h4>
<p>진짜입니다. 그냥 맨땅에 헤딩입니다.
여기서 계속 하는 말이 있습니다. &quot;<strong>인터넷과 매뉴얼과 동료들</strong>에게 물어보세요~!&quot;
네, 정말 그 세 개만 동아줄마냥 붙들고 해야됩니다.</p>
<p>내가 전공자다, 하면 괜찮은데, 비전공자다, C 언어 초면이다, 하면 안괜찮아요. (경험담)
정말 염치 불구하고 그저 고수분들 팔 잡고 늘어져야 합니다. 살아남으려면 페르소나 제대로 만들고 질문해야 합니다!</p>
<h4 id="2-팀플이-있음">2. 팀플이 있음</h4>
<p>매 주 팀플이 있는데, 강제로 해야 하는 건 아닙니다. 자기가 신청하는 것!
저는 마지막 팀플 빼고 다 했는데, 하면서 도움이 정말 많이 됐습니다. 좋은 친구들도 사귀었고, 문제를 풀면서, 팀원들에게 알려주면서, 평가 받으면서 쑥쑥 성장하는 느낌이 들었습니다.</p>
<p>저는 세 팀플 모두 긍정적인 경험을 하고 좋은 결과를 냈지만, 그렇지 않은 분들도 많이 계셨어요. 마지막 팀플과 달리 앞의 팀플들은 조원을 랜덤으로 배정해주기 때문에 소위 &#39;운빨&#39;이 정말 심합니다. 그리고 2주차 팀플부터 난이도가 엄청 올라갑니다.
전공자분들도 던지는 문제가 나오기도 하니 <strong>자기 페이스나 멘탈에 따라</strong> 달리 하시는 게 좋을 것 같습니다.</p>
<h4 id="3-시험이-있음">3. 시험이 있음</h4>
<p>매 주 시험도 있습니다. 이 또한 강제는 아닌데 안할 이유가 없긴 하죠?
시험은(특히 첫 시험은) 그냥 멘탈 와장창입니다. 첫 시험은 입구컷 당하는 사람이 정말정말정말 많습니다.</p>
<p>저는 운 좋게도 시험 시작을 하긴 했고, 또 C 언어를 공부했어서 출발이 좋았습니다. 근데 C 언어를 안다고 해서 다 푸는 것도 아니고, 모른다고 해서 무조건 입구컷 당하는 것도 아닙니다. 다만, <strong>시험을 시작한 이상 C 언어를 모르는 것보단 아는 게 무조건 좋습니다.</strong></p>
<p>시험은 그냥 가서 맞닥뜨려보는게 좋을 것 같아요. 저는 시험 시작하고서도 한시간 넘게 문제를 못 풀었습니다. 왜 그런진 시험을 보면 알게 될 것...</p>
<h4 id="4-돈을-줌">4. 돈을 줌</h4>
<p>출석 시간 채우면 돈 줍니다!
교육기간 끝나고 주는 것 같습니다!
<strong>자세한건 사무국</strong>으로 나문희1</p>
<h4 id="5-한-달-동안-죽어라-해야-함">5. 한 달 동안 죽어라 해야 함</h4>
<p>병행 가능한가요? 라는 QnA를 많이 본 것 같은데, 만약 제게 병행 가능한지 물으신다면 <strong>&#39;본과정 가고 싶으면 한 달 올인해라&#39;</strong> 라고 답변하고 싶습니다.</p>
<p>망할 전염병 때문에 격일로 출석해서 솔직히 죽어라까진 아니었지만, 저는 출석하는 날엔 아침부터 막차까지 자리를 지켰습니다. 왕복 두 시간의 이동 시간에도 계속 개인과제, 팀과제 생각을 했습니다. 물론 원격 날에도 스터디룸에 박혀있었고요.</p>
<p>생각보다 한 달이 짧고, 내가 해결해야 하는 과제가 많습니다!</p>
<h4 id="6-본과정-합격에-관한-뇌피셜들">6. 본과정 합격에 관한 뇌피셜들</h4>
<p>엄청 많은 것 같은데, 저는 그냥 그거 신경쓰지 말고 나 알아서 잘하면 된다는 주의입니다. 한 달, 격일로 클러스터 나가다 보면 시간이 정말 아까워요. 뇌피셜 쫓느라 시간 허비하지 마시고 내가 맞다고 생각하는 길로 그냥 나가시면 되는 것 같습니다!</p>
<p>근데 이렇게 말하면 너무 재수없죠? 하하... 그렇지만 피시너들 사이에서 나오는 말들은 뇌피셜이기 때문에 하나하나 연연하지 마시고, 큰 틀에서 어떤 사람이 뽑힐까를 생각해보면 좋을 것 같습니다.</p>
<p>기업에서 원하는 인재상, 기업 문화에 잘 융화될 것 같은 사람을 뽑듯, 42 서울도 교육 목표와 교육 환경에 잘 맞는 사람을 뽑아내는 것 같아요. 그걸 염두하고, 내 소신대로 하다보면 좋은 결과 있을 겁니다!</p>
<h3 id="라피신-꿀팁">라피신 꿀(?)팁</h3>
<p><img src="https://images.velog.io/images/seri_ous/post/11b3a822-0913-45a2-9e79-117ba048e5e8/arwin-neil-baichoo-yQzrDgU-KAI-unsplash.jpeg" alt="">
과제나 시험에 대해서 가장 궁금하시겠지만 비밀유지 서약을 했기 때문에.. 라고 연막치지만 사실 저 두 부분은 알아서 헤쳐나가시는 것이 맞는 것 같아 얘기하지 않도록 하겠습니다!</p>
<p>그 대신 한 달 동안 혼돈의 도가니 속에서 남들보다 더 빨리 적응할 수 있는 꿀팁을 이야기해보려고 합니다. (꿀팁이 맞는지 아닌진 모르겠지만...)</p>
<h4 id="1-웃으면-무조건-이득">1. 웃으면 무조건 이득</h4>
<p>라 피신 동안 웃는 얼굴을 디폴트로 다녔습니다.</p>
<p>처음 사람들을 만나서 인사할 때도, 밥을 먹을 때도, 프로필 사진을 찍을 때도, 동료 평가를 받거나 할 때도, 팀플을 할 때도, 질문을 할 때도 그냥 웃었습니다. 덕분에 사람들과 편안한 분위기 속에서 과제를 하고 평가를 할 수 있었습니다. 웃는 얼굴에 침 못 뱉는다고, 즐거운 분위기에서 마친 동료평가에서 대체적으로 긍정적인 피드백을 써주신 것 같습니다.</p>
<p>특히, 프로필 사진을 찍을 때 꼭 웃으세요. 공장처럼 찍혀나가느라 어색할 테지만, 그 때 한 번 잘 웃어놓으면 나중에 정말정말정말! 좋습니다.
(본과정 합격 후에도 프로필 사진 쭉 간다는 건 안비밀)</p>
<h4 id="2-난-너처럼-못웃는다">2. 난 너처럼 못웃는다</h4>
<p>맞습니다. 모두가 웃을 수 있는 건 아니에요.</p>
<p>나는 진짜 웃는게 안되고, 웃음이 안난다. 나는 되게 소극적이고 남에게 뭘 부탁하는게 어렵고, 분위기 좋게 만드는 게 어렵다... 라는 사람들이 있다면, 작은 초콜릿이나 젤리 등 당충전 간식류를 구비하시는 것을 추천드립니다.</p>
<p>물론 클러스터 내에선 코로나 때문에 못 먹지만, 그래도 누가 간식을 쥐어주는데 싫어할 사람은 없을 거예요! 저 역시 이렇게 챙겨주셨던 분들과 기분 좋게 평가와 팀프로젝트를 진행했던 기억이 아직도 남아 있습니다.</p>
<h4 id="3-깃과-깃허브">3. 깃과 깃허브</h4>
<p>깃 사용법을 아는 것과 모르는 것은 맥 os에 익숙하냐 안익숙하냐와 동일한 정도입니다. 오히려 깃 사용법이 큰 틀에선 더 유용합니다!</p>
<p>시간이 된다면, 공부 하는 겸 깃허브 계정을 만들어서 공부한 내용을 올려보세요! 저는 앞서 얘기했던 사이드카 깔면서 처음으로 깃 클론 어쩌구를 해보고, 개발자들은 다 1일 1커밋 한다는 말에 바로 깃허브 가입해서 터미널로 뭐든 커밋 했습니다.</p>
<p>그게 도움될 줄은 전혀 몰랐는데, 피신 전반의 과정에서 유용하게 쓰였습니다.</p>
<h4 id="4-삼시세끼와-꿀잠">4. 삼시세끼와 꿀잠</h4>
<p>긴 말 않겠습니다.
잘 챙겨 드시고, 잘 주무세요! <strong>(최고 중요)</strong></p>
<h4 id="5-텀블러와-치약-칫솔">5. 텀블러와 치약, 칫솔</h4>
<p>하루 종일 클러스터에 있을 피시너의 필수템 두 가지를 꼽는다면 저 두 가지를 꼽겠어요.</p>
<p>자리에서 물 또한 마실 수 없어 클러스터에 &#39;오아시스&#39;라는 물 먹는 공간이 따로 마련 돼 있습니다. 제 정신을 책임져줄 카페인이 시원하게 보관되기 위해선 보냉이 잘 되는 대용량 텀블러만한 게 없습니다.</p>
<p>+) 저는 안 썼는데, 슬리퍼도 꿀템이래요.</p>
<h4 id="6-모르는데-어떻게-해요">6. 모르는데 어떻게 해요</h4>
<p>어려운 문제 하나가 며칠 동안 내 발목을 잡는다면?</p>
<p>&#39;모르는데 어떻게 풀어&#39; 라는 마인드로 쿨하게 넘어가는 것도 괜찮은 방법입니다. <strong>진심으로.</strong>
(100점과 1등만 알아주는 K 학습과정 속에서 자라와서 불-편 한 건 인정하지만, 그래도 일단 붙고 봐야죠!)</p>
<h4 id="7-고비">7. 고비</h4>
<p>3~4주차때 고비가 옵니다.</p>
<p>저는 4주차때 공부를 위해 코드를 짜는 게 아니라, 레벨업을 해야 해서 코드를 짜야 된다는 게 너무 힘들었습니다.</p>
<p>집중도 안되고, 문제 풀기가 어려울 땐 동료평가를 다니면서 사람들과 이야기도 하고, 새로운 코드를 보며 리프레쉬도 하는 걸 추천합니다.</p>
<h4 id="8-경험자">8. 경험자</h4>
<p>주변에 라 피신을 경험해본 경험자가 있다면 커피 한 잔 때리면서 꿀팁 전수받으세요. 없다면?  댓글 주세요!</p>
<hr>
<h3 id="졸려서-하는-급-마무리">졸려서 하는 급 마무리</h3>
<p><img src="https://images.velog.io/images/seri_ous/post/09288162-8f64-4160-ae24-4912ecce24db/maxresdefault.jpeg" alt="">
라피신 진짜 재밌습니다. 진짜 힘든데 재밌어요.</p>
<p>그냥 진짜 수영장에 던져놓고 알아서 헤엄쳐 나와야 하는 환경 자체가 재밌습니다. 객관적으로 그게 재밌는 건 아닌 것 같고, 저랑 잘 맞았던 것 같아요. 도전정신과 독립심이 강한 분이시라면 분명 저처럼 재밌다고 느끼실 겁니다.
(생각해보니 객관적으로 재밌는 일들이 4기 2차, 저희 그룹에 많이 터지긴 했던 것 같네요.)</p>
<p>솔직히 후기에 &#39;못 붙어도 귀중했던 한 달&#39;이라는 말을 보고서 의심이 돋았는데 하고 나니 그 말이 이해가 됐습니다.</p>
<p>저는 제가 모르는 부분을 아는 전공자들과, 저와 같이 성장하고자 하는 비전공자들이 정말 필요했어요. 혼자 공부하는 건 정말 답답하고 힘든 일이었습니다. 그래서 피신 기간 동안 제가 모르는 걸 알려준 고수들, 같이 모르는 걸 찾아나갔던 동료들을 만난 게 정말 큰 행운이라고 생각해요.</p>
<p><img src="https://images.velog.io/images/seri_ous/post/27e6e720-cc03-45bf-90f6-614ee08bc7fa/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-04-27%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.18.07.png" alt="">
제가 요즘 가장 동의하면서 동의하지 않는 짤입니다.
늦었다고 생각할 때도 안 늦었고, 늦었어도 당장 시작하면 된다는 생각으로 살고 있고, 하던 일을 그만두고 새로운 길로 가려는 친구들에게 항상 그렇게 말합니다.</p>
<p>흔한 비전공자가 개발자 되고 싶어서 방황하는 테크를 타본(그리고 여전히 타고 있는중인) 제가 이 분야로 노선을 바꾸고 그 첫 단계로 피신을 끝낸 후 단언할 수 있는 건, 생각하지 말고 그냥 일단 뭐라도 정해서 해보는게 이득이라는 것입니다.</p>
<p>42 서울이 아니더라도, ssafy든 우아한 테크 코스든 부스트코스든 국비학원이든 부트캠프든 대학원이든 대학교든 전과든 취업이든 뭐든간에 그냥 내가 이야기를 나누고, 지식을 쌓을 수 있는 사람들과 그룹에 들어가 시작하는 게 가장 빠른 길이라고 생각합니다.</p>
<p>저도 갓 출발한 사람이라 결과를 확신할 순 없지만, 어떤 이유에서건 이 길이 맞겠다 생각이 들면 당장 한달이라도 달려보는걸 추천합니다. 시작을 응원합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아 이거 진짜 노가다 해야돼? 라는 문과 샘숭 지원자에게... (도움 안됨 주의)]]></title>
            <link>https://velog.io/@seri_ous/%EC%95%94%EA%B2%83%EB%8F%84-%EB%AA%A8%EB%A5%B4%EA%B3%A0-%EA%B7%B8%EC%A0%80-%EB%85%B8%EA%B0%80%EB%8B%A4%ED%95%98%EB%A0%A4%EB%8A%94-%EB%AC%B8%EA%B3%BC-%EC%83%98%EC%88%AD-%EC%A7%80%EC%9B%90%EC%9E%90-%EC%9E%A0%EA%B9%90-%EC%99%80%EB%B3%B4%EC%8A%88</link>
            <guid>https://velog.io/@seri_ous/%EC%95%94%EA%B2%83%EB%8F%84-%EB%AA%A8%EB%A5%B4%EA%B3%A0-%EA%B7%B8%EC%A0%80-%EB%85%B8%EA%B0%80%EB%8B%A4%ED%95%98%EB%A0%A4%EB%8A%94-%EB%AC%B8%EA%B3%BC-%EC%83%98%EC%88%AD-%EC%A7%80%EC%9B%90%EC%9E%90-%EC%9E%A0%EA%B9%90-%EC%99%80%EB%B3%B4%EC%8A%88</guid>
            <pubDate>Thu, 18 Mar 2021 09:53:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>삼성 이수교과목 매크로 찾다가 번번히 실패한 뒤 &#39;내가 매크로를 만들어보겠다!&#39; 하고 나섰다가 일일히 기입한 것보다 시간 더 걸린.ssul</p>
</blockquote>
<h3 style="color:red">주의! 도움 안될 수 있음</h3>
<div style="color:grey">어쨌든 노가다는 해야됩니다...ㅎ</div>

<hr>
<h3 id="이게뭐야">이게뭐야</h3>
<p>삼성 입사지원서 써보려고 들어갔다가 이수교과목 기입란에 기함했다.</p>
<p>아니 뭐 이런..</p>
<p>대학 동안의 모든 이수 교과목들의 년도, 학기, 학점, 기타등등 모든 정보를 저장해야 했는데, 심지어 탭키로 쉽게 움직일 수 없었다.
그냥 마우스로 딸깍딸깍 하면서 일일히 다 체크해야하는 것...</p>
<h4 id="매크로-검색--실패">매크로 검색 &amp; 실패^^!</h4>
<p>이 무슨 비효율인가 싶어서, 바로 구글에 &#39;이수교과목 매크로&#39;를 검색했다.
역시나 개발짱님들이 몇 가지의 매크로를 만들어서 이미 공유하고 있었다.</p>
<p>하지만 문제는 내가 그 매크로들을 사용할 수 없다는 거였는데...</p>
<p>가장 유명(?)한 거로 보여지는 매크로는 작성자가 서버를 요청이 있을 때만 열었다가 평소엔 닫아 현재 굳게 닫혀있는 상태였다. 또 다른 것들은 윈도우용이거나, 이번년도 버전의 입사지원서에는 제대로 적용되지 않는 것들이었다.</p>
<hr>
<h3 id="내-수준에서-꼼수쓰기">내 수준에서 꼼수쓰기</h3>
<p>포기하고 일일이 칠까 하다가 아무래도 오바인 것 같아서 나름대로 꼼수를 써봤다.
(물론 그 꼼수가 엄청난건 아니지만) 어쨌든 원하는 만큼의 리스트를 만들어놓고, 탭 키를 이용해 데이터를 수정할 수 있는 방식까지,,, 알아냈다. ** (2021년 상반기 기준)**</p>
<h4 id="html-수정하기">html 수정하기</h4>
<p>너무 당연하지만 막상 &#39;입사지원서인데 웹페이지 수정이 될까..?&#39;하는 의구심에 시도해보지 않은 &#39;html 직접 뜯어 수정하기&#39;를 했다.</p>
<p>유명한 매크로 작성!자가 블로그에 사용방법을 제시한 걸 바탕으로, &lt;thead&gt; 태그에 내가 원하는 정보들이 있음을 알았다.</p>
<h4 id="thead-태그-찾기">thead 태그 찾기!</h4>
<p>입사지원서 이수교과목 페이지에서 마우스 우클릭, 개발자 도구를 열어 눈이 팽팽 도는 html 태그들을 무시하고 command(ctrl)+F 로 thead 태그를 찾자.
id가 <strong>majdetTable인 table 아래 thead</strong>를 찾을 수 있었따!
<img src="https://images.velog.io/images/seri_ous/post/0172daee-8e98-4262-9ccb-dc03cf240ca7/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-03-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.29.05.png" alt=""></p>
<h4 id="tr-태그--정보-복붙하기">tr 태그 &amp; 정보 복붙하기!</h4>
<p>  우선 삼성 입사지원서가 하라는 대로 리스트 하나를 만들자.</p>
<p>  그 다음 리스트의 html 정보_(&lt;tr&gt;태그에 있다)_를 긁어 원하는 수만큼 복사 붙여넣기를 하면 리스트가 불어나 있다.</p>
<p>  저장하고 로그아웃, 시크릿모드로 테스트 해본 결과 모두 정상적으로 리스트 수가 증가한 것을 확인할 수 있었다. 근데 이제 하나씩 수정해야 하는 노가다를 곁들인...
<img src = "https://images.velog.io/images/seri_ous/post/c30228f7-be93-4514-9a91-54281a79e298/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-03-18%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.45.48.png" width="200px"></p>
<hr>
<h3 id="ddong싸고-안닦은-것-같은-결론">ddong싸고 안닦은 것 같은 결론</h3>
<p>  이제 키보드만 이용해 지원서를 수정할 수 있게 됐다. 와! 하하...</p>
<p>  <strong>tab키</strong>로 항목 이동, <strong>방향키</strong>로 옵션 선택, <strong>return(enter)</strong>키로 옵션 지정, 그리고 과목이름 입력 하면 된다.</p>
<h4 id="흑흑-살려줘">흑흑 살려줘</h4>
<p>  근데 이걸로는 부족하다고 느낀 나는 열심히 삽질을 해본다.</p>
<p>  아직까지 진행중... 일단 엑셀로 정리한 데이터를 활용해 2015 -&gt; option에서 2015 찾아서 선택... 뭐 이런 코드를 짜고싶은데 어렵다.</p>
<p>  그래서 결론은? 응 노가다...
  근데 진짜 이거 왜 노가다 시키는거예요..? 너무궁금함..</p>
<p>아무튼 그냥 진짜 덮어놓고 클릭하기 너무 싫다 하시는 분들은 한 번 따라해 보세요.</p>
<h4 style="color:red;">!! 이 꼼수에 대한 책임은 따라하는 본인에게 있습니다.</h4>
+ 더 빠르고 획기적인 방법 가지고 계신 스승님들 가르침 부탁드립니다...]]></description>
        </item>
        <item>
            <title><![CDATA[C언어 복습2 - 배열]]></title>
            <link>https://velog.io/@seri_ous/C%EC%96%B8%EC%96%B4-%EB%B3%B5%EC%8A%B52-%EB%B0%B0%EC%97%B4</link>
            <guid>https://velog.io/@seri_ous/C%EC%96%B8%EC%96%B4-%EB%B3%B5%EC%8A%B52-%EB%B0%B0%EC%97%B4</guid>
            <pubDate>Mon, 15 Mar 2021 12:53:26 GMT</pubDate>
            <description><![CDATA[<h3 id="배열과-응용">배열과 응용</h3>
<h4 id="배열">배열</h4>
<ul>
<li>같은 자료형의 변수를 일렬로 늘어놓은 형태다.</li>
<li>int arr[10] = {1, 2, 3, ... , 9, 10}; 과 같이 선언하고 초기화한다. 한 번 {} 를 이용해 초기화된 배열은 다시 {} 를 이용해 초기화할 수 없다.</li>
<li>배열의 인덱스는 0부터 시작한다. 즉, 배열의 길이가 10일 때, 배열의 인덱스는 0부터 시작해 9로 끝난다.</li>
<li>배열과 포인터는 동일하게 작동하기 때문에, 배열의 인덱스로 특정 위치의 값을 알 수 있는 것처럼, 포인터도 인덱스를 통해 특정 위치의 값에 접근할 수 있다.</li>
<li>배열의 크기는 sizeof(arr)/sizeof(int) 로 구할 수 있다.</li>
</ul>
<h4 id="다차원-배열">다차원 배열</h4>
<h5 id="2차원-배열">2차원 배열</h5>
<ul>
<li>int arr[r][c]; 과 같이 표현되며, r 과 c 는 각각 세로크기, 가로크기다. 세로크기는 행의 개수(row)이고, 가로크기는 열의 개수(column)이다.</li>
<li>int (*ptr)[c]; 는 2차원 배열을 넣을 수 있는 포인터다.</li>
<li>이 때, (*ptr)[c] 와 *ptr[c] 를 꼭 잘 구분해야 한다! 전자는 배열의 포인터, 후자는 포인터 배열이다.</li>
<li>세로크기, 가로크기를 구하는 공식은 다음과 같다
sizeof(arr)/sizeof(arr[0]);
sizeof(arr[0])/sizeof(int);</li>
</ul>
<h5 id="3차원-배열">3차원 배열</h5>
<ul>
<li>int arr[d][r][c]; 와 같이 표현되며, d, r, c 는 각각 깊이, 세로크기, 가로크기다. 평면 구조인 2차원 배열과 달리 3차원 공간으로 표현되며, 그 구분이 맨 앞 [d]에 따른다는 점을 기억하면 된다.</li>
<li>(*ptr)[r][c]; 는 3차원 배열을 넣을 수 있는 포인터다.</li>
<li>높이, 세로크기, 가로크기
sizeof(arr)/sizeof(arr[0]); =&gt; 배열의 전체를 한 면의 크기로 나누기
sizeof(arr[0])/sizeof(arr[0][0]);
sizeof(arr[0][0])/sizeof(int);</li>
</ul>
<h3 id="포인터를-배열처럼-사용">포인터를 배열처럼 사용</h3>
<ul>
<li>포인터에 메모리를 할당한 뒤, []에 인덱스를 저장하면 배열처럼 사용할 수 있다.<pre><code>int *ptr = malloc(sizeof(int) * n);</code></pre></li>
<li>2차원 배열처럼 사용하려면, 이중 포인터를 선언해 세로 공간을 만들어준 뒤, 반복문을 통해 가로 공간을 만들어주면 된다.</li>
<li>메모리 해제는 역순으로(반복문으로 가로공간 해제-&gt;세로공간 해제) 하면 된다.<pre><code>int m, n (각 세로크기, 가로크기)
int **ptr = malloc(sizeof(int*)*m);
for(int i=0; i&lt;m; i++){
  ptr[i] = malloc(sizeof(int)*n);
}</code></pre></li>
<li>3차원 배열처럼 사용하려면, 면 -&gt; 세로공간 -&gt; 가로공간 순으로 메모리를 할당해주면 된다.<pre><code>int h, m, n (각 높이, 세로크기, 가로크기);
int ***ptr = malloc(sizeof(int**)*h);
for(int i=0; i&lt;h; i++){
  ptr[i] = malloc(sizeof(int*)*m);
  for(int j=0; j&lt;m; j++){
      ptr[i][j] = malloc(sizeof(int)*n);
  }
}</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C언어 복습1 - 포인터]]></title>
            <link>https://velog.io/@seri_ous/C%EC%96%B8%EC%96%B4-%EB%B3%B5%EC%8A%B51-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@seri_ous/C%EC%96%B8%EC%96%B4-%EB%B3%B5%EC%8A%B51-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Mon, 15 Mar 2021 06:32:45 GMT</pubDate>
            <description><![CDATA[<h3 id="포인터와-메모리">포인터와 메모리</h3>
<h4 id="int--ptr">int * ptr</h4>
<ul>
<li>포인터는 메모리 공간을 만들어 변수의 주소를 저장하는 역할을 한다.</li>
<li>* 연산자를 사용하며, 저장할 변수의 자료형에 따라 포인터의 자료형도 바뀐다.</li>
<li>int * ptr 는 pointer to int 라고 부르며, 말 그대로 int 자료형 정수의 메모리 주소를 가리키는 포인터라는 뜻을 가지고 있다.</li>
<li>포인터의 크기는 32비트 컴퓨터에서 4바이트, 64비트 컴퓨터에서 8바이트다.</li>
<li>포인터는 메모리 공간을 가리키는 역할이기 때문에, 그 자체로 변수를 대입해 초기화하거나 사칙연산 등의 연산을 할 수 없다.</li>
<li>즉, 포인터를 하나의 자료형으로 본다면, int *ptr에 대입할 수 있는 변수는 &#39;주소&#39;를 가리키는 변수인 것이다. 이를 나타내는 방법으로 &#39;&amp;&#39; 연산자를 사용한다. 이는 address to int 라고 부르며, pointer to int 와 본질적으로 같다.</li>
</ul>
<pre><code>    int num = 10;
        int *ptr = &amp;num;
        printf(&quot;address of num: %p\n&quot;, &amp;num);
        printf(&quot;value of ptr: %p\n&quot;, ptr);
        // 위 두 printf 값은 동일한 메모리 주소가 나온다.</code></pre><ul>
<li>포인터의 주소가 아닌 &#39;저장된 값&#39;을 가져오고 싶다면, 역참조 연산자인 &#39;*&#39;를 사용하면 된다.</li>
<li>*ptr = num; 과 같이 쓸 수 있으며, ptr이 가리키는 메모리 안에 저장돼 있는 값을 가져오는 것이다.</li>
</ul>
<h4 id="void-ptr">void *ptr</h4>
<ul>
<li>void 포인터는 자료형이 정해지지 않은 포인터다.</li>
<li>int, double, float, long long 등 모든 자료형과 호환가능하다.</li>
<li>하지만 자료형이 정해지지 않은 포인터이니만큼 메모리의 크기를 알 수 없어 역참조를 할 수 없다.</li>
</ul>
<h4 id="이중-포인터">이중 포인터</h4>
<ul>
<li>*연산자를 n번 쓰면 n중 포인터가 되고, 차례대로 pointer-&gt;pointer-&gt;pointer-&gt;...-&gt;num 을 가리키게 된다.</li>
<li>이중 포인터의 값에 접근하기 위해 역참조 연산자를 쓸 때도 두 개를 써야 한다.</li>
</ul>
<h4 id="malloc">malloc</h4>
<ul>
<li>메모리를 동적으로 할당할 때 사용하는 메모리 생성 함수로, &lt;stdlib.h&gt; 헤더 파일에 포함돼있다.</li>
<li>malloc 함수의 매개변수로는 할당할 메모리의 크기가 들어가고, 메모리를 사용하고 난 뒤, 꼭 free 함수를 사용해 메모리를 해제해줘야 한다.</li>
<li>malloc 함수를 통해 할당된 메모리는 &#39;heap&#39; 영역에 저장된다 (변수는 &#39;stack&#39;).<pre><code>  int * ptr1 = malloc(sizeof(int));
      char * ptr2 = malloc(sizeof(char)*4+1);
      int num = 10;
      ptr1 = &amp;num;
      ptr2 = &quot;dope&quot;;
      free(ptr1);
      free(ptr2);</code></pre></li>
</ul>
<h4 id="memset">memset</h4>
<ul>
<li>메모리를 특정 값으로 채울 때 사용하는 함수로, &lt;string.h&gt; 헤더 파일에 포함돼있다.</li>
<li>memset(ptr, 0, sizeof(int)) 등과 같이 사용한다.</li>
</ul>
<h4 id="null-potiner">NULL potiner</h4>
<ul>
<li>아무 것도 가리키지 않는 포인터를 널 포인터라고 하며, 
int * ptr = NULL; 과 같이 선언/초기화 한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>