<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sean.log</title>
        <link>https://velog.io/</link>
        <description>"잘 할 수 있을까?"를 고민하기보단 재밌어 보이는건 일단 하고, 잘하기 위해 그냥 계속합니다.</description>
        <lastBuildDate>Fri, 20 Mar 2026 03:33:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sean.log</title>
            <url>https://velog.velcdn.com/images/sean_kk/profile/1776a053-ece4-4a66-a17f-086fd18049f8/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sean.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sean_kk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Claude Dispatch(Cowork) - 폰으로 컴퓨터에 있는 AI를 원격 조종해봤다]]></title>
            <link>https://velog.io/@sean_kk/Claude-DispatchCowork-%ED%8F%B0%EC%9C%BC%EB%A1%9C-%EC%BB%B4%ED%93%A8%ED%84%B0%EC%97%90-%EC%9E%88%EB%8A%94-AI%EB%A5%BC-%EC%9B%90%EA%B2%A9-%EC%A1%B0%EC%A2%85%ED%95%B4%EB%B4%A4%EB%8B%A4</link>
            <guid>https://velog.io/@sean_kk/Claude-DispatchCowork-%ED%8F%B0%EC%9C%BC%EB%A1%9C-%EC%BB%B4%ED%93%A8%ED%84%B0%EC%97%90-%EC%9E%88%EB%8A%94-AI%EB%A5%BC-%EC%9B%90%EA%B2%A9-%EC%A1%B0%EC%A2%85%ED%95%B4%EB%B4%A4%EB%8B%A4</guid>
            <pubDate>Fri, 20 Mar 2026 03:33:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<hr>
<h2 id="요약">요약</h2>
<blockquote>
<p>Claude Desktop의 Dispatch(Cowork) 기능을 직접 
써봤다.</p>
<ul>
<li>폰에서 컴퓨터에 있는 Claude에게 원격으로 일을 시킬 수 있다.</li>
<li>파일 만들기, 브라우저 조작, 예약 작업까지 된다.</li>
<li>결론: 손발이 달린 Claude. 범용 비서가 따로 없다.</li>
</ul>
</blockquote>
<hr>
<h2 id="갑자기-웬-dispatch">갑자기 웬 Dispatch?</h2>
<p>Claude Desktop을 쓰다 보면 Cowork 모드라는게 있다. 이게 뭐냐면, 그냥 웹 Claude처럼 대화만 하는게 아니라 진짜로 내 컴퓨터에서 뭔가를 <strong>실행</strong>까지 해주는 기능이다.</p>
<p>코드 실행, 파일 생성/편집, 브라우저 직접 조작(Chrome), 로컬 파일 접근, 문서 만들기(Word, Excel, PPT, PDF 등), 심지어 예약 작업 설정까지 가능하다.</p>
<p>그중에서도 Dispatch라는 기능이 있는데, 이게 좀 신기해서 직접 써보고 정리를 해봤다.</p>
<blockquote>
<p>한 줄 요약: &quot;손발이 달린 Claude&quot;로, 대화 + 실행까지 되는 범용 비서 역할이다.</p>
</blockquote>
<hr>
<h2 id="폰으로-컴퓨터를-조종한다고">폰으로 컴퓨터를 조종한다고?</h2>
<p>이게 좀 미친 부분이다.</p>
<p>Claude 앱(iOS/Android)을 설치하면 Dispatch 탭이 있는데, 여기서 내 컴퓨터에 있는 Cowork에 작업 지시를 날릴 수 있다.
밖에 나가 있어도 폰으로 &quot;이거 해줘&quot;하면 집에 있는 컴퓨터에서 Claude가 알아서 작업하는거다.</p>
<p>다만 알아둬야 할 것들이 있다:</p>
<ul>
<li>폰 → 컴퓨터: 가능</li>
<li>컴퓨터1 → 컴퓨터2: 불가능</li>
<li>원격 데스크톱 같은게 아니라 텍스트 기반 지시 &amp; 결과 수신 방식</li>
</ul>
<p>뭐 쉽게 말하면 폰은 리모컨이고 Claude가 실행자인 셈이다.
<del>(OpenClaw 같은 별도 도구 없이도 이게 된다는게 핵심)</del></p>
<hr>
<h2 id="직접-써보니-이런-느낌이었다">직접 써보니 이런 느낌이었다</h2>
<p>실제로 Dispatch에 &quot;우리가 얘기한 내용 전부 정리해서 드릴게요&quot;라고 시키니까 바로 Word 문서를 만들기 시작했다.</p>
<p>근데 여기서 한 가지 문제가 생겼다. 저렇게 세션으로 만든건 폰에서 직접 확인이 불가능하다는 것이다. 
Claude가 바로 &quot;그러면 Google Docs로 만들어드릴까요?&quot; 라고 제안을 해왔고 이번에는 Chrome 브라우저를 직접 열어서 Google Docs에 문서를 만들겠다고 한다.</p>
<p>웹에서 사용하려면 이렇게 크롬에 확장 프로그램을 설치 해야 한다.
<img src="https://velog.velcdn.com/images/sean_kk/post/c7ceec66-bd20-43a7-9204-991581176b64/image.png" alt=""></p>
<p>이게 뭐가 인상적이었냐면, Claude가 그냥 시키는 것만 하는게 아니라 내 상황에 맞춰서 대안을 제시하고 바로 실행까지 한다는 거다.</p>
<h3 id="chrome-확장-프로그램-연결">Chrome 확장 프로그램 연결</h3>
<p>다만 Google Docs에 접근하려면 Claude in Chrome 확장 프로그램이 필요했다. 설치 과정은 이랬다.</p>
<ol>
<li><p>Chrome에서 Claude 확장 프로그램 설치
<img src="https://velog.velcdn.com/images/sean_kk/post/e0e8c076-16c0-4493-9a2d-088a1e7423f4/image.png" alt=""></p>
</li>
<li><p>Claude 계정 연동 승인
<img src="https://velog.velcdn.com/images/sean_kk/post/34bd446c-8b56-4fc5-b050-4c2316337514/image.png" alt=""></p>
</li>
<li><p>설치 완료 후 브라우저 탐색 준비 화면
<img src="https://velog.velcdn.com/images/sean_kk/post/e3f7e948-eb51-4fc7-8350-f10064205b96/image.png" alt=""></p>
</li>
</ol>
<p>한 번만 해두면 이후로는 Claude가 브라우저를 직접 조작해서 Google Docs든 뭐든 알아서 해준다.
생각보다 괜찮은 트레이드오프였다.</p>
<h3 id="결과물-google-docs로-정리-완료">결과물: Google Docs로 정리 완료</h3>
<p>그렇게 해서 나온 결과물이 이거다.
내용이 중요한게 아니라 그냥 이렇게 까지 해준다를 보여주고 싶었다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/0b32b127-4a27-40a2-a916-e6601e4fb809/image.png" alt=""></p>
<p>그리고 이 내용들을 토대로 이 블로그를 작성하기로 했다.</p>
<p>Dispatch란 무엇인지부터 폰 원격 제어, 세션 구조, 기존 Cowork과의 차이, 컨텍스트 관리, 핵심 요약까지 6개 섹션으로 깔끔하게 정리가 되었다.
내가 대화에서 했던 내용들을 알아서 구조화해서 문서로 만들어준거다.</p>
<hr>
<h2 id="세션-구조는-어떻게-되어있나">세션 구조는 어떻게 되어있나</h2>
<p>Dispatch의 세션 구조를 이해하면 좀 더 효율적으로 쓸 수 있다.</p>
<ul>
<li><strong>메인 세션(Dispatch)</strong> = 관제탑 역할</li>
<li>실제 작업은 <strong>별도 태스크 세션</strong>에서 처리 (깨끗한 상태에서 시작)</li>
<li>여러 작업 동시 실행 가능</li>
</ul>
<p>그러니까 메인 세션에서 &quot;이거 해줘&quot;, &quot;저거도 해줘&quot; 이렇게 지시를 날리면 각각 독립적인 세션에서 처리가 되는거다.
하나가 느려도 다른건 상관없이 돌아간다.</p>
<p>코워크 쪽에서 보면 디스패치에서 만든 세션은 이렇게 따로 만들어진게 보인다.
<img src="https://velog.velcdn.com/images/sean_kk/post/f4cd211d-bac9-4424-ae6f-d637894edb20/image.png" alt=""></p>
<hr>
<h2 id="기존-cowork-프로젝트와-뭐가-다른건데">기존 Cowork 프로젝트와 뭐가 다른건데?</h2>
<p>나는 기존에 Cowork 프로젝트로 로컬 프로젝트를 작업하고 있었다.
그래서 처음에 Dispatch가 나왔을 때 &quot;이거 기존 Cowork이랑 뭐가 다른건데?&quot; 싶었다.</p>
<p>직접 써보니 차이가 꽤 명확했다:</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>기존 Cowork 프로젝트</th>
<th>Dispatch</th>
</tr>
</thead>
<tbody><tr>
<td>용도</td>
<td>프로젝트별 디렉토리에 상주하며 코드 작업에 특화</td>
<td>범용 작업용, 여러 프로젝트를 넘나들며 잡다한 일 처리</td>
</tr>
<tr>
<td>강점</td>
<td>무거운 코드 작업</td>
<td>문서 만들기, 브라우저 조작, 예약 작업 등</td>
</tr>
<tr>
<td>느낌</td>
<td>전문 개발자</td>
<td>만능 비서</td>
</tr>
</tbody></table>
<p>결론적으로 무거운 코드 작업은 기존 Cowork 프로젝트가 더 유리하고, 나머지 범용 작업은 Dispatch로 하는게 맞는 느낌이었다.</p>
<hr>
<h2 id="컨텍스트-관리-일명-치매-방지">컨텍스트 관리 (일명: 치매 방지)</h2>
<p>AI 도구를 오래 쓰다 보면 대화가 길어지면서 앞에서 했던 말을 까먹는 경우가 있다. <del>(나도 까먹지만 AI도 까먹는다)</del></p>
<p>Dispatch에서는 이 부분이 좀 낫다:</p>
<ul>
<li>태스크 세션이 독립적이라 메인 대화 영향을 덜 받음</li>
<li>메모리 시스템으로 중요 정보를 파일로 저장 가능</li>
<li>너무 길어지면 새 세션 시작 권장</li>
</ul>
<p>완전히 방지된 건 아니지만, 세션이 분리되어 있다는 것 자체가 꽤 큰 장점이었다. 기존에 하나의 대화창에서 모든걸 하던 것보다 확실히 안정적이다.</p>
<hr>
<h2 id="마치며">마치며</h2>
<p>직접 써보니 Dispatch는 확실히 기존 Cowork과는 결이 다른 도구였다. 코드를 짜는 용도라기보다는 진짜 &quot;비서&quot;에 가깝다.</p>
<p>특히 인상적이었던 그냥 시키는 것만 하는게 아니라 상황에 맞게 판단하고 대안을 실행하는 것, 이게 단순한 챗봇과의 차이점이었다.</p>
<p>나 같은 경우에는 이제 코드 작업은 기존 Cowork 프로젝트에서, 문서 작업이나 브라우저 조작 같은 잡다한 일은 Dispatch에서 처리하는 방식으로 쓸 생각이다.</p>
<blockquote>
<p>별도 도구 없이 Claude 하나로 밖에서도 로컬 컴퓨터 작업이 가능하다. 이게 핵심이다.</p>
</blockquote>
<h3 id="참고자료">참고자료</h3>
<ul>
<li>Claude Desktop Cowork 공식 문서</li>
<li>직접 사용하면서 정리한 내용</li>
</ul>
<h3 id="기타">기타</h3>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[GPT Pro Codex vs Claude Code: 클린 환경에서 붙여본 지극히 주관적인 비교]]></title>
            <link>https://velog.io/@sean_kk/GPT-Pro-Codex-vs-Claude-Code-%ED%81%B4%EB%A6%B0-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%B6%99%EC%97%AC%EB%B3%B8-%EC%86%94%EC%A7%81-%EB%B9%84%EA%B5%90-ebs0gvya</link>
            <guid>https://velog.io/@sean_kk/GPT-Pro-Codex-vs-Claude-Code-%ED%81%B4%EB%A6%B0-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EB%B6%99%EC%97%AC%EB%B3%B8-%EC%86%94%EC%A7%81-%EB%B9%84%EA%B5%90-ebs0gvya</guid>
            <pubDate>Fri, 13 Feb 2026 16:47:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<hr>
<h2 id="사전-말씀">사전 말씀</h2>
<p>지극히 주관적인거고 뭔가 딥하게 설정을 잡아보고 한 것도 아니고 &quot;아 그렇구나~&quot; 정도로만 보면된다.
Codex 와 Claude Code의 방식이 완전히 다르고 이제 각각 그 고유 설정들을 다 잡아주면 아예 다른 결과가 나올 수 도 있으니까 말이다.</p>
<hr>
<h2 id="요약">요약</h2>
<blockquote>
<p>클린 환경에서 GPT Pro(카톡)와 Claude Code를 붙여봤다.</p>
<ul>
<li>문서/기획/요약: Codex 우세</li>
<li>코드 안정성과 완성도: Claude Code 우세</li>
<li>디자인 감성: Codex 압승</li>
</ul>
</blockquote>
<blockquote>
<p>근데 결론은 둘 다 <strong>그냥 미쳤다</strong> 라는거다 뭘 사용 하든 추가 작업이 필수라서 그것들을 해주고 사용면 그냥 다 최고의 결과를 뽑아낼 것이다.</p>
</blockquote>
<hr>
<h2 id="왜-이-비교를-했나">왜 이 비교를 했나</h2>
<p>이번에 카카오톡에서 ChatGPT Pro를 비교적 부담 없는 가격에 풀었고 나는 바로 결제했다. 하지만 나는 기존에 잘 쓰고 있던 Claude가 있었고 Claude Code와 실제 작업에서 얼마나 차이가 나는지 확인하고 싶었다. </p>
<h2 id="실험-환경클린-세팅">실험 환경(클린 세팅)</h2>
<ul>
<li>기존 세팅 전부 제거: CLAUDE.md, AGENTS.md, 스킬, MCP, 서브 에이전트</li>
<li>빈 프로젝트에서 시작</li>
<li>모델 기본 능력만으로 결과 확인</li>
</ul>
<hr>
<h2 id="비교-절차">비교 절차</h2>
<ol>
<li>최신 모델(+think)에게 “수박게임을 아냐?” 질문</li>
<li>웹 게임 PRD.md 생성 요청</li>
<li>Codex/Claude Code에 줄 프롬프트 설계 질문</li>
<li>빈 프로젝트에서 PRD + 프롬프트로 개발 시작</li>
</ol>
<ul>
<li>시작
<img src="https://velog.velcdn.com/images/sean_kk/post/2f38b36b-c3c7-4d5d-bbea-1d3928bb8e8f/image.png" alt=""></li>
</ul>
<hr>
<h2 id="결과-요약시간">결과 요약(시간)</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>시간</th>
</tr>
</thead>
<tbody><tr>
<td>시작</td>
<td>10:35:57</td>
</tr>
<tr>
<td>Claude Code 완료</td>
<td>10:44:52</td>
</tr>
<tr>
<td>Codex 완료</td>
<td>11:00:51</td>
</tr>
</tbody></table>
<p>시간만 보면 Claude Code가 더 빠르다. 하지만 핵심은 완성도였다.</p>
<ul>
<li>Claude Code 결과물
<img src="https://velog.velcdn.com/images/sean_kk/post/b65a6dec-7d5d-4ae3-92a6-4bceaeaad861/image.png" alt=""></li>
<li>Codex 결과물
<img src="https://velog.velcdn.com/images/sean_kk/post/1244f8fe-166e-46f0-9969-514f0c068f49/image.png" alt=""></li>
</ul>
<hr>
<h2 id="디자인은-codex가-압승-안정성은-claude-code">디자인은 Codex가 압승, 안정성은 Claude Code</h2>
<ul>
<li>Codex 결과물은 디자인이 훨씬 세련됨</li>
<li>하지만 오류 발생 + 게임 동작 불일치</li>
<li>Claude Code는 오류 없이 정상 동작, 다만 디자인은 무난한 수준</li>
</ul>
<p>결론적으로 코딩 안정성과 완성도는 Claude Code, UI/디자인 감성은 Codex 쪽이 더 강했다.</p>
<ul>
<li><p>좌측이 Codex / 우측이 Claude Code
<img src="https://velog.velcdn.com/images/sean_kk/post/a9ac1726-5099-4453-b31a-5d6d7c102bf7/image.png" alt=""></p>
</li>
<li><p>게임오버 화면인데 코덱스로 만든건 오류가 있는것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/sean_kk/post/b7f284b4-5338-4d3d-8672-0eca433c3d4d/image.png" alt=""></p>
</li>
</ul>
<hr>
<h2 id="codex-prd--claude-프롬프트-재실험">Codex PRD + Claude 프롬프트 재실험</h2>
<ul>
<li><p>작업 시간: 11:15:07 ~ 11:32:43</p>
</li>
<li><p>결과: 오류 없이 정상 동작</p>
</li>
<li><p>단점: 디자인은 여전히 무난</p>
</li>
<li><p>결과물</p>
</li>
</ul>
<table>
<thead>
<tr>
<th>시작</th>
<th>튜토리얼</th>
<th>게임 오버</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/sean_kk/post/b3ca1065-27f5-4fe5-9fc7-864e87b40639/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/sean_kk/post/73393393-6bd0-493b-921f-4b94777834a2/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/sean_kk/post/5a3d5d9c-ae9b-4a44-b1c3-5673e2cb64d3/image.png" alt=""></td>
</tr>
</tbody></table>
<hr>
<h2 id="문서화-비교">문서화 비교</h2>
<p>이전에 작업하던 프로젝트가 하나 있어서 이것을 분석하는 작업을 시켜봤다.</p>
<table>
<thead>
<tr>
<th>항목</th>
<th>Codex</th>
<th>Claude Code</th>
</tr>
</thead>
<tbody><tr>
<td>문서 구조</td>
<td>개요 → 상세 흐름이 깔끔</td>
<td>분석용 형식, 읽기 피로도 큼</td>
</tr>
<tr>
<td>사용자 배려</td>
<td>높음</td>
<td>낮음</td>
</tr>
<tr>
<td>읽기 편함</td>
<td>좋음</td>
<td>아쉬움</td>
</tr>
</tbody></table>
<p>정리 느낌은 Codex가 확실히 더 좋았다.</p>
<hr>
<h2 id="결론-지극히-주관적-기준">결론 (지극히 주관적 기준)</h2>
<ul>
<li>세팅 없이 바로 쓸 때: 문서 작성/기획/계획 수립은 Codex가 유리</li>
<li>실제 개발/코딩 단계: Claude Code가 더 안정적</li>
</ul>
<p>다만 이건 완전 클린 상태 기준이다.
AGENTS.md, CLAUDE.md, 스킬, MCP, 서브 에이전트 등을 세팅하면 둘 다 결과가 크게 달라질거다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/fec77afe-b762-4e54-94fa-062ed3aa5e4c/image.png" alt="">
한 줄 요약:
문서를 만들고 설계도를 그리는 단계는 Codex, 그 설계도를 실제 코드로 옮기는 단계는 Claude Code.</p>
<hr>
<h3 id="마치며">마치며</h3>
<p>이번 비교는 철저히 내 주관과 실험 환경에 기반한 기록이다.
그리고 동작하는 방식도 완전 다를거고 그리고 그냥 대충 던져준거 하나로 비교라고 하기에는 둘 다 너무 뛰어나니 딱히 뭐가 안좋다라고는 하기에는 좀 그래도 직접 해보니 그 차이가 있었다.</p>
<p>그래서 개인적으로 둘 다 사용할 거 같은데 이제 그 의도를 잘 맞춰서 사용해볼 생각이다.</p>
<h3 id="참고자료">참고자료</h3>
<h3 id="기타">기타</h3>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SPMS] 4. 설계의 본질 : AI에게 '완벽한 지도'를 쥐여주는 법]]></title>
            <link>https://velog.io/@sean_kk/SPMS-4.-%EC%84%A4%EA%B3%84%EC%9D%98-%EB%B3%B8%EC%A7%88-AI%EC%97%90%EA%B2%8C-%EC%99%84%EB%B2%BD%ED%95%9C-%EC%A7%80%EB%8F%84%EB%A5%BC-%EC%A5%90%EC%97%AC%EC%A3%BC%EB%8A%94-%EB%B2%95-2i0sqdzg</link>
            <guid>https://velog.io/@sean_kk/SPMS-4.-%EC%84%A4%EA%B3%84%EC%9D%98-%EB%B3%B8%EC%A7%88-AI%EC%97%90%EA%B2%8C-%EC%99%84%EB%B2%BD%ED%95%9C-%EC%A7%80%EB%8F%84%EB%A5%BC-%EC%A5%90%EC%97%AC%EC%A3%BC%EB%8A%94-%EB%B2%95-2i0sqdzg</guid>
            <pubDate>Tue, 10 Feb 2026 05:38:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<p>지난 포스팅에서 도커(Docker)를 활용해 인프라라는 무대를 다 지어놨으니, 이제 바로 코딩이라는 연기를 시작할 줄 알았다. 하지만 막상 키보드에 손을 올리니 망설여졌다.</p>
<p>솔직히 말하면, 나는 이 프로젝트에 들어가는 모든 기술과 언어에 통달한 전문가가 아니다. 세상에 공부할 건 너무 많고, 내 리소스는 한정적이다. 그런데도 내가 대용량 B2B 푸시 솔루션이라는 거창한 목표에 도전할 수 있는 이유는 명확하다. 나에게는 <strong>Claude Code</strong>라는 아주 유능한 &#39;팀원&#39;이 있기 때문이다.</p>
<p>이번 4편에서는 그 유능한 팀원을 제대로 부리기 위해, 내가 그 어느 때보다 문서 작성과 설계에 집착했던 과정을 적어보려 한다.</p>
<h3 id="언어는-도구일-뿐-핵심은-설계design다">언어는 도구일 뿐, 핵심은 설계(Design)다</h3>
<p>많은 사람이 &quot;어떤 언어로 짤까?&quot;를 고민할 때, 나는 &quot;어떻게 가치를 설계할까?&quot;를 더 고민했다. AI와 협업해 본 사람이라면 알 것이다. 내 의도가 흐리멍덩하면 AI가 뱉어내는 코드도 결국 쓰레기가 된다는 것을.</p>
<p>1인 개발자로서 AI에게 코드를 맡긴다는 것은, 내가 &#39;타이피스트&#39;가 아닌 &#39;아키텍트&#39;가 되어야 함을 의미한다. AI가 내 의도를 100% 이해하고 버그 없는 코드를 내놓게 하려면, 코드를 한 줄도 치기 전에 이미 문서 위에서 시스템이 완벽하게 돌아가고 있어야 한다.</p>
<h3 id="ai-수석-설계자들과의-티키타카">AI 수석 설계자들과의 티키타카</h3>
<p>이번 설계 과정에서는 <strong>Gemini</strong>와 <strong>Claude</strong>를 내 팀원으로 고용했다.</p>
<ul>
<li><strong>Gemini</strong>와는 거시적인 비즈니스 로직을 논의했다. &quot;B2B 푸시 서비스라면 어떤 기능이 필수적이지?&quot;라는 질문을 던져 PRD(제품 요구사항 문서)의 뼈대를 잡았다.</li>
<li><strong>Claude</strong>는 그 뼈대에 살을 붙이는 역할을 맡겼다. &quot;이 기능이 저 요구사항이랑 충돌하지 않아??&quot;라고 끈질기게 물으며 74개의 요구사항을 하나하나 검증하고 다듬었다.</li>
</ul>
<p>이렇게 탄탄하게 다져진 요구사항 정의서와 기능 명세서는 이제 Claude Code가 한 치의 오차도 없이 코드를 구현해낼 수 있는 &#39;완벽한 지도&#39;가 되었다.</p>
<h3 id="미래의-나-그리고-ai를-위한-배려">미래의 나, 그리고 AI를 위한 배려</h3>
<p>내가 설계 문서와 씨름하며 유난을 떤 이유는 결국 두 가지다.</p>
<ol>
<li><strong>미래의 나를 위해</strong>: 나중에 내가 이 코드를 고칠 때 과거의 나를 원망하지 않도록. (내가 나를 보좌하는 셈이다.)</li>
<li><strong>AI를 위해</strong>: AI가 맥락을 정확히 짚어 수준 높은 결과물을 내놓을 수 있도록 &#39;정답지&#39;를 미리 만들어두는 과정이다.</li>
</ol>
<p>결국 <strong>언어는 도구일 뿐이다.</strong> 진정한 개발의 가치는 그 도구를 어떻게 휘두를지 결정하는 <strong>설계(Design)와 흐름(Flow)</strong>에 있다는 것을 이번 기회에 다시 한번 느꼈다.</p>
<h3 id="마치며">마치며</h3>
<p>설계도는 이제 완벽하게 준비되었다. 이제 이 지도를 들고 본격적으로 &#39;구현&#39;이라는 실전에 들어갈 차례다. 과연 이 치밀한 설계도가 실제 코드에서는 어떻게 꽃을 피울지, 다음 화부터 하나씩 풀어보겠다.</p>
<h3 id="참고자료">참고자료</h3>
<h3 id="기타">기타</h3>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SPMS] 3. 인프라 구축의 서막 (고생의 시작) : 컨테이너 빌드]]></title>
            <link>https://velog.io/@sean_kk/SPMS-3.-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%B6%95%EC%9D%98-%EC%84%9C%EB%A7%89-%EA%B3%A0%EC%83%9D%EC%9D%98-%EC%8B%9C%EC%9E%91-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B5%AC%EB%8F%99</link>
            <guid>https://velog.io/@sean_kk/SPMS-3.-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%B6%95%EC%9D%98-%EC%84%9C%EB%A7%89-%EA%B3%A0%EC%83%9D%EC%9D%98-%EC%8B%9C%EC%9E%91-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EA%B5%AC%EB%8F%99</guid>
            <pubDate>Fri, 02 Jan 2026 01:05:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--내-경험--ai">참고자료 : 내 경험 + AI</h4>
</blockquote>
<h1 id="시작">시작</h1>
<p>이전의 <a href="https://velog.io/@sean_kk/SPMS-2.-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%B6%95%EC%9D%98-%EC%84%9C%EB%A7%89-%EA%B3%A0%EC%83%9D%EC%9D%98-%EC%8B%9C%EC%9E%91-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1">2편</a>에서 머리를 싸매고 작성한 yaml 파일을 만들었다면 이제 실제로 환경이 분리되게 도커를 만드는 작업을 해볼것이다.</p>
<p>하다가 안되면 계속 수정하고 하면서 이 포스팅에는 최종적으로 성공한 것들을 올릴것이다.</p>
<h1 id="docker">Docker</h1>
<h2 id="저장-위치-설정">저장 위치 설정</h2>
<p>다른 분들은 어떻게 했는지는 모르겠는데 일단 나는 다음과 같이 파일 구조를 잡았다.
<img src="https://velog.velcdn.com/images/sean_kk/post/0b71e57e-ca45-46cb-9674-1c3abf538e2b/image.png" alt="">
PROJECT 안에 모든 프로젝트 코드들이 들어갈거고 DOCUMENT 안에는 이 프로젝트의 문서들이 들어갈거다.
(근데 실제 문서 작성은 여기가 아니라 구글 문서들로 작성을 하고 있다.)</p>
<h2 id="컨테이너-설정">컨테이너 설정</h2>
<p>나와 같은 시놀리지 나스를 사용하고 있다면 컨테이너 매니저 앱에서 해당 과정을 똑같이 진행하면 된다.</p>
<ul>
<li><p>컨테이너가 아니라 프로젝트에서 진행을 한 이유는 이렇게 하면 한 번에 이 프로젝트 전체를 관리하기가 편해서 해당 방법을 사용했다.
<img src="https://velog.velcdn.com/images/sean_kk/post/0a856458-bb4b-419e-b04c-6fa3e885d123/image.png" alt=""></p>
</li>
<li><p>프로젝트 이름 잘 쓰고 경로는 위에 맞춰준것처럼 SPMS 라고 가장 최상단쪽을 맞춰주면 된다.
<img src="https://velog.velcdn.com/images/sean_kk/post/3168ac93-31a3-458a-9e8d-5dbe8f5645a0/image.png" alt=""></p>
</li>
<li><p>그리고 이제 2편에서 작성(각자 프로젝트에 맞게 변경한)한 코드를 만들어주면 된다.
<img src="https://velog.velcdn.com/images/sean_kk/post/231193b7-f447-44b5-a799-bfc075003af2/image.png" alt=""></p>
</li>
<li><p>다음
<img src="https://velog.velcdn.com/images/sean_kk/post/6dec93d0-e4e2-4f5b-8c08-ba7afa833f28/image.png" alt=""></p>
</li>
<li><p>다음
<img src="https://velog.velcdn.com/images/sean_kk/post/1fe31c61-fbca-4653-a922-2394345b6282/image.png" alt=""></p>
</li>
<li><p>완료를 누르면 이렇게 프로젝트가 생성된것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/sean_kk/post/7fa65b4d-0fda-4aad-828b-8031076021e9/image.png" alt=""></p>
</li>
<li><p>생성된 프로젝트에 들어가서 
<img src="https://velog.velcdn.com/images/sean_kk/post/4d7fb0c1-5833-4ddf-957b-444a4c62c29f/image.png" alt=""></p>
</li>
<li><p>우상단 보면 [작업] 버튼이 있는데 여기서 빌드 버튼을 눌러준다.
<img src="https://velog.velcdn.com/images/sean_kk/post/dd6bbdce-1390-45cc-a683-f4f86b09f879/image.png" alt=""></p>
</li>
<li><p>빌드 중
<img src="https://velog.velcdn.com/images/sean_kk/post/801c1b2d-05f0-4884-9434-2ae7c67fbefe/image.png" alt=""></p>
</li>
<li><p>빌드 끝이 나면서 오류가 발생하는데 당연한게 지금 서버나 웹의 코드가 아무것도 없으니 빌드하거나 런할 대상이 없어서 오류가 나는거니 일단은 무시하고 넘어가면 된다.
(만약 이렇게 말고 다른 오류가 난다면 문제가 있는거니 해결을 해야 한다.)
<img src="https://velog.velcdn.com/images/sean_kk/post/dc6b6564-8b27-41d0-92ea-ed5c9c9f8013/image.png" alt=""></p>
</li>
<li><p>최종적으로 소스코드나 다른게 필요 없는 DB 정도만 잘 만들어지고 컨테이너가 켜져 있는 것을 볼 수 있다.
(만약에 래빗과 레디스도 넣어서 했으면 그거도 켜져 있을건데 일단 이 캡쳐를 찍을때는 그거 없이 빌드 했던거라 현재 상황과 이 캡쳐가 다르다고 해도 딱히 신경 안써도 된다.)
<img src="https://velog.velcdn.com/images/sean_kk/post/d02e0304-04be-4a8e-845a-4f52be093043/image.png" alt=""></p>
</li>
</ul>
<h2 id="마치며">마치며</h2>
<p>일단 이정도까지만 해서 컨테이너 구현은 끝났고 이제 저장위치에 코드를 올리는건 각자 자신의 방법을 사용하면된다.</p>
<p>소스코드를 잘 옮겼다면 종료를 하고 다시 빌드를 누르거나 컨테이너 별로 실행을 해주면 되긴 하는데 뭐 이 방법에 대해서도 차차 풀어볼 생각이다.</p>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SPMS] 2. 인프라 구축의 서막 (고생의 시작) : 환경 분리의 중요성]]></title>
            <link>https://velog.io/@sean_kk/SPMS-2.-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%B6%95%EC%9D%98-%EC%84%9C%EB%A7%89-%EA%B3%A0%EC%83%9D%EC%9D%98-%EC%8B%9C%EC%9E%91-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</link>
            <guid>https://velog.io/@sean_kk/SPMS-2.-%EC%9D%B8%ED%94%84%EB%9D%BC-%EA%B5%AC%EC%B6%95%EC%9D%98-%EC%84%9C%EB%A7%89-%EA%B3%A0%EC%83%9D%EC%9D%98-%EC%8B%9C%EC%9E%91-%ED%99%98%EA%B2%BD-%EB%B6%84%EB%A6%AC%EC%9D%98-%EC%A4%91%EC%9A%94%EC%84%B1</guid>
            <pubDate>Fri, 02 Jan 2026 00:22:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--내-경험--ai">참고자료 : 내 경험 + AI</h4>
</blockquote>
<h1 id="시작">시작</h1>
<p>지난 1편에서 NAS에 Docker를 올리고 Jenkins를 통해 CI/CD 파이프라인을 구축하는 것으로 결정을 했다. 그럼 이제 이에 대한 개발을 시작해야 하는데 가장 먼 든 생각은 일단 개발과 운영을 분리를 하자 였다.</p>
<p>혼자쓰는 사이드 프로젝트라지만 엄염히 운영중인 서비스와 내가 막 코드를 수정하고 개발하는 개발 환경은 분리를 해야한다. 안 그러면 로컬에서 테스트 한다고 데이터를 지우거나 했을때 실제 운영 서비스가 바뀌는 대참사가 일어날 테니까 말이다.</p>
<p>그래서 본격적으로 뭔가를 구현하기에 앞서서 Docker Compose 하나로 빌드, 배포하고 환경 분리까지 처리하는 방법에 대해 정리하려 한다.</p>
<hr>
<hr>
<h1 id="본문">본문</h1>
<h2 id="왜-한다고">왜 한다고?</h2>
<p>사이드라고 개발과 운영을 같이 섞어서 쓰는 순간 당장에 개발은 가능할지라도 후에는 답이 없어진다.
지금처럼 단순하게 만들기만 하는게 아닌 후에 운영도 해볼 생각이라면 분리를 하는게 맞다</p>
<p>뭐 물론 포스팅 하는것처럼 작성을 해보자면 이러한 이유들을 댈 수 있을것이다.</p>
<ol>
<li>데이터 오염 방지 : 테스트 데이터와 실 데이터가 섞이는 것 방지 가능</li>
<li>안정성 확보 : 개발 서버의 테스트로 서버가 뻗어도 운영 서버는 살아있을 수 있다.</li>
<li>보안 : 개발용 API Key와 운영용 API Key는 철저히 분리</li>
</ol>
<p>그리고 지금처럼 내가 NAS라는 하나의 물리 장비 안에서 이 모든 걸 돌려야 하기에 물리적인 격리를 할 수 없으니 논리적인 격리가 더더욱 중요했다.</p>
<h2 id="구성">구성</h2>
<p>논리적인 격리가 중요하기에 이 문제를 해결하기 위해 Docker Compose와 .env 파일을 적극적으로 활용해보기로 했다.
다른 방법이 있을지도 모르겠지만 일단 내가 알고 있는 건 이 방법이기에 이걸로 하기로 했다.</p>
<p>일단 나스에서 컨테이너 매니저에서 프로젝트에 compose.yaml 파일을 만들어서 서비스를 정의하며 환경에 따라 실행되는 컨테이너를 다 다르게 가져갔다.</p>
<h3 id="전략">전략?</h3>
<p>전략? 이라고 해야할까? 뭐라고 지칭을 해야 할지 모르겠어서 일단 전략이라고 하긴 했는데 일단 compose.ymal을 구성하면서 어떤걸 중점으로 할지 정했다.</p>
<h4 id="전략1-build--run-컨테이너-분리">전략1. Build / Run 컨테이너 분리</h4>
<p>이미지를 매번 로컬에서 빌드해서 올리는게 아니라 변경된 소스코드만 올려두면 NAS의 컨테이너를 사용해서 알아서 빌드하게 만들었다.
이렇게 하면 불필요한 빌드 도구들이 런타임 컨테이너에 남지 않게 되니 가볍고 깔끔하다 생각했다.</p>
<ol>
<li>Build 컨테이너 : 소스 코드를 마운트해서 빌드만 수행하고 죽는 컨테이너 - 빌드 결과는 볼륨을 통해서 공유</li>
<li>Run 컨테이너 : 빌드된 결과물을 받아서 실행만 하는 가벼운 컨테이너</li>
</ol>
<h4 id="전략2-debug--release-병렬-구성">전략2. Debug / Release 병렬 구성</h4>
<p>Compose 내에 두가지 환경을 모두 정의를 했다.
이게 진짜 환경 분리를 한 지점이라 보면 된다. 그리고 디버그용과 릴리즈용 두가지 환경에서 각각 빌드 컨테이너와 런 컨테이너 2개가 다 돌아가게 되어있다.</p>
<hr>
<h3 id="yaml-코드">yaml 코드</h3>
<p>이거도 접기 써서 하면 블로그는 깔끔해질거 같은데 벨로그는 이걸 지원 안한다니까..</p>
<p>일단 코드만 그대로 올려두겠다.</p>
<p>지금 아래 있는 코드가 내가 사용하는 실제 코드랑은 많이 다를건데 잼미니한테 말해서 개인적인 정보 및 포트 사용하면서 바꾼것들 이름들 싹다 바꾼 그냥 가장 기본적인 내 방식에 맞는 코드니까 만약에 이걸 따라하실 분들이 계시다면 아래 코드를 가져다가 잘 바꿔서 써보면 된다.</p>
<p>코드에 대한 설명은 딱히 할 건 없을거 같고 이제 이걸 사용해서 실제 도커를 구동하는건 다음 포스트에 작성해보겠다.</p>
<pre><code class="language-yaml">version: &#39;3.8&#39;

networks:
  app-network:
    driver: bridge

services:
  # ==============================================================================
  # [1] DEBUG 환경 (개발용)
  # ==============================================================================  
  frontend-build-debug:
    container_name: frontend-build-debug
    image: node:22-alpine
    working_dir: /src
    volumes:
      - ./Client:/src
      - ./Server:/back
      - /src/node_modules
    command: &gt;
      sh -c &quot;\
      rm -rf node_modules package-lock.json &amp;&amp; \
      npm install &amp;&amp; \
      npm run build &amp;&amp; \
      echo &#39;[D] React Build DONE&#39; &amp;&amp; \
      rm -rf /back/publish/debug/wwwroot/* || true &amp;&amp; \
      mkdir -p /back/publish/debug/wwwroot &amp;&amp; \
      cp -av /src/dist/. /back/publish/debug/wwwroot &amp;&amp; \
      echo &#39;[D] Copy DONE&#39; \
      &quot;
    restart: &quot;no&quot;
    networks:
      - app-network
    env_file:
      - ./config/env/web.dev.env

  backend-build-debug:
    container_name: backend-build-debug
    image: mcr.microsoft.com/dotnet/sdk:9.0
    working_dir: /src
    volumes:
      - ./Server:/src
    command: &gt;
      sh -c &quot;
      # MyApp.csproj는 본인의 프로젝트 파일명으로 변경
      dotnet publish /src/MyApp/MyApp.csproj -c Debug -o /src/bin/Debug/net9.0
      &quot;
    restart: &quot;no&quot;
    networks:
      - app-network

  backend-run-debug:
    container_name: backend-run-debug
    image: mcr.microsoft.com/dotnet/aspnet:9.0 
    working_dir: /src/bin/Debug/net9.0
    environment:
      ASPNETCORE_ENVIRONMENT: &quot;Development&quot;
      ASPNETCORE_WEBROOT: &quot;/src/publish/debug/wwwroot&quot;
      ASPNETCORE_HTTP_PORTS: 8080
      ASPNETCORE_URLS: &quot;http://+:8080&quot;
    volumes:
      - ./Server:/src
      - ./Logs/API_Debug:/app/logs
    ports:
      - &quot;8080:8080&quot;
    # 실행할 dll 파일명을 본인 프로젝트에 맞게 수정
    command: [&quot;dotnet&quot;, &quot;MyApp.dll&quot;]
    restart: &quot;no&quot;
    networks:
      - app-network
    depends_on:
      backend-build-debug:
        condition: service_completed_successfully
      frontend-build-debug:
        condition: service_completed_successfully
      database:
        condition: service_started
    env_file:
      - ./config/env/api.dev.env

  # ==============================================================================
  # [2] RELEASE 환경 (배포용)
  # ==============================================================================
  frontend-build-release:
    container_name: frontend-build-release
    image: node:22-alpine
    working_dir: /src
    volumes:
      - ./Client:/src
      - ./Server:/back
      - /src/node_modules
    command: &gt;
      sh -c &quot;\
      rm -rf node_modules package-lock.json &amp;&amp; \
      npm install &amp;&amp; \
      npm run build &amp;&amp; \
      echo &#39;[R] React Build DONE&#39; &amp;&amp; \
      rm -rf /back/publish/release/wwwroot/* || true &amp;&amp; \
      mkdir -p /back/publish/release/wwwroot &amp;&amp; \
      cp -av /src/dist/. /back/publish/release/wwwroot &amp;&amp; \
      echo &#39;[R] Copy DONE&#39; \
      &quot;
    restart: &quot;no&quot;
    networks:
      - app-network
    env_file:
      - ./config/env/web.prod.env

  backend-build-release:
    container_name: backend-build-release
    image: mcr.microsoft.com/dotnet/sdk:9.0
    working_dir: /src
    volumes:
      - ./Server:/src
    command: &gt;
      sh -c &quot;
      dotnet publish /src/MyApp/MyApp.csproj -c Release -o /src/bin/Release/net9.0
      &quot;
    restart: &quot;no&quot;
    networks:
      - app-network

  backend-run-release:
    container_name: backend-run-release
    image: mcr.microsoft.com/dotnet/aspnet:9.0
    working_dir: /src/bin/Release/net9.0
    environment:
      ASPNETCORE_ENVIRONMENT: &quot;Production&quot;
      ASPNETCORE_WEBROOT: &quot;/src/publish/release/wwwroot&quot;
      ASPNETCORE_HTTP_PORTS: 80
      ASPNETCORE_URLS: &quot;http://+:80&quot;
    volumes:
      - ./Server:/src
      - ./Logs/API_Release:/app/logs
    ports:
      - &quot;80:80&quot;
    command: [&quot;dotnet&quot;, &quot;MyApp.dll&quot;]
    restart: &quot;no&quot;
    networks:
      - app-network
    depends_on:
      backend-build-release:
        condition: service_completed_successfully
      frontend-build-release:
        condition: service_completed_successfully
      database:
        condition: service_started
    env_file:
      - ./config/env/api.prod.env

  # ==============================================================================
  # [3] DB (MariaDB)
  # ==============================================================================
  database:
    container_name: mariadb-server
    image: mariadb:latest
    restart: &quot;no&quot;
    volumes:
      - ./DB/config:/etc/mysql/conf.d
      - ./DB/data:/var/lib/mysql
      - ./Logs/DB:/var/log/mysql
    environment:
        TZ: Asia/Seoul
        MYSQL_ROOT_PASSWORD: &quot;${DB_ROOT_PASSWORD}&quot; 
        MYSQL_TCP_PORT: 3306
    ports:
      - &quot;3306:3306&quot;
    command: --log-error=/var/log/mysql/mariadb_error.log
    networks:
      - app-network

  # ==============================================================================
  # [4] RabbitMQ
  # ==============================================================================
  rabbitmq:
    container_name: rabbitmq-server
    hostname: rabbitmq
    image: rabbitmq:3-management
    restart: &quot;no&quot;
    environment:
      RABBITMQ_LOG_BASE: /var/log/rabbitmq
    env_file:
      - ./config/env/api.prod.env
    ports:
      - &quot;5672:5672&quot;   
      - &quot;15672:15672&quot; 
    volumes:
      - ./MQ/data:/var/lib/rabbitmq
      - ./Logs/MQ:/var/log/rabbitmq
    networks:
      - app-network

  # ==============================================================================
  # [5] Cache (Redis)
  # ==============================================================================
  redis:
    container_name: redis-server
    image: redis:alpine
    restart: &quot;no&quot;
    command: redis-server --appendonly yes --logfile /var/log/redis/redis.log --requirepass &quot;${REDIS_PASSWORD}&quot;
    ports:
      - &quot;6379:6379&quot;
    volumes:
      - ./Redis/data:/data
      - ./Logs/Redis:/var/log/redis
    networks:
      - app-network</code></pre>
<h2 id="마치며">마치며</h2>
<p>생각보다 코드가 너무 길어서 실제 만드는건 다음 포스팅에 해야 할 거 같다.</p>
<hr>
<hr>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[배포] 애플 비공개 앱 배포를 진행하면서 2 - (비공개 앱 배포)]]></title>
            <link>https://velog.io/@sean_kk/%EB%B0%B0%ED%8F%AC-%EC%95%A0%ED%94%8C-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%98%EB%A9%B4%EC%84%9C-2-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@sean_kk/%EB%B0%B0%ED%8F%AC-%EC%95%A0%ED%94%8C-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%98%EB%A9%B4%EC%84%9C-2-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Mon, 29 Dec 2025 07:14:02 GMT</pubDate>
            <description><![CDATA[<p>블로그</p>
<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--apple-unlisted-app-distribution-가이드">참고자료 : <a href="https://developer.apple.com/support/unlisted-app-distribution/">Apple Unlisted App Distribution 가이드</a></h4>
</blockquote>
<h1 id="이어서">이어서</h1>
<p><a href="https://velog.io/@sean_kk/%EB%B0%B0%ED%8F%AC-%EC%95%A0%ED%94%8C-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%98%EB%A9%B4%EC%84%9C-1-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%95%A0%ED%94%8C%EC%9D%98-%EC%95%B1-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EB%B2%95">이전 글</a>에서는 애플의 앱 배포 방식에 대해서 알아봤는데 그런거보다는 실질적으로 비공개 앱 배포를 하는 과정이 중요한거니까 이번 게시글에는 그 과정에 대해서 작성할 것이다.</p>
<p><em>며칠 동안 일정이 좀 있어서 포스트가 밀렸다.</em></p>
<h1 id="심사-준비">심사 준비</h1>
<h2 id="준비물">준비물</h2>
<p>준비할게 몇가지 필요하다.</p>
<ol>
<li>배포할 앱</li>
<li>개발자 유료 플랜 가입된 계정</li>
<li>영어 잘하는 두뇌 또는 AI = (AI 추천)</li>
</ol>
<h2 id="비공개-앱-신청서-제출">비공개 앱 신청서 제출</h2>
<p><a href="https://developer.apple.com/support/unlisted-app-distribution">비공개 앱 관련 문서</a> 를 먼저 확인을 해보면 아래와 같은 페이지가 나온다.
<img src="https://velog.velcdn.com/images/sean_kk/post/5a3fd7f8-1fbb-4c72-8576-d862bfb99fef/image.png" alt="">
위 이미지에 있는것처럼 <strong>Requesting an unlisted app link</strong> 이 색션을 보면 파란 하이퍼링크로 <strong>submit a request</strong> 가 있는데 이 링크로 이동한다.</p>
<p><a href="https://developer.apple.com/contact/request/unlisted-app/">submit a request</a> 물론 이 하이퍼 링크를 눌러도 이동이 가능하다.</p>
<p>그럼 이렇게 아래처럼 좀 길<del>게 작성을 하는 페이지가 나오게 되는데 현재 자신의 상황에 맞게 잘 작성해주면 된다.
_</del>(그냥 이 페이지 전문을 긁어서 AI 주고 현재 상황 설명하면 잘 작성해줌)~~_</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/dce34cac-8fd4-4a29-9809-7937b2692463/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/88742757-2241-433e-8a18-68b9ee020610/image.png" alt=""></p>
<p>참고로 내가 할때는 <strong><em>&quot;현재 우리가 비즈니스 계정을 신청하기에는 규모도 작고 mdm 배포로 하기에는 해당 배포 시스템 구축을 하지 않아서 이런 방법밖에 없으며 이게 각 개인의 기기에 다운로드를 해야 하는 시스템이기에 ABM과도 맞지 않고 다량의 기기를 관리하는데도 어려움이 있어서 이렇게 배포를 신청하게 되었다.&quot;</em></strong> 라는 식으로 비공개 앱 배포의 취지에 맞게 잘 상황에 맞게 바꿔서 작성을 해주었다.</p>
<hr>
<h2 id="앱-심사-제출">앱 심사 제출</h2>
<p>뭐 따로 말할게 있을까 싶을 정도로 일반 심사랑 똑같이 제출하면 된다.
스크린샷 첨부하고, 앱 설명 쓰고, 앱 연령 제한 잘 맞추고, 권한 같은거 잘 선택넣고, 등등
이제 중요한건 메모에다가 <em><strong>지금 비공개 앱 신청서를 제출한 상태이며 이 앱은 일반 배포가 아니라 비공개 앱 배포가 필요한 앱이다.</strong></em> 라고 써 두는게 중요하다.</p>
<p>이걸 안해두면 이런 리젝을 받게 된다.
<img src="https://velog.velcdn.com/images/sean_kk/post/2e52f00b-fa7b-498a-939b-c3a03a4ef5f1/image.png" alt=""></p>
<p>이 앱은 상용앱이 아니라 비즈니스 앱이니까 방식 바꾸라는 뭐 그런 리젝 문구이다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/5098f48a-70c5-4001-b7fd-6399e910be67/image.png" alt=""></p>
<p>그래서 이렇게 &quot;미안미안~ 신청서 넣었어~&quot; 라고 하는 답변 도 보냈었다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/826a01fc-a332-420a-84fd-d5c7d5a8a3c9/image.png" alt=""></p>
<p>근데 어쨌든 아직 위에서 작성한 신청서의 승인이 안되서 아직 기다리라는 이런 회신을 다시 받게 되는데 이때는 그냥 기다리는 수 밖에 없다. <strong>비공개 앱 신청서</strong> 이게 승인이 안나면 앱 심사도 진행이 되지 않기 때문이다.
(영업일 3일 내라고 하던데, 진짜 영업일 3일 걸렸다... <del>이번 신청서 말고 이전에 테스트로 한 번 보내 본적이 또 있었는데 그때도 영업일 3일 걸렸었다.</del>)</p>
<hr>
<h2 id="비공개-앱-신청서-승인">비공개 앱 신청서 승인</h2>
<p>비공개 앱 신청서가 승인이 나게 된다면 이제 개발자등록 메일로 승인이 되었다고 메일이 오게 된다. (승인이든 거절이든)
<img src="https://velog.velcdn.com/images/sean_kk/post/a1f30505-93c2-4a55-9709-967ebdc3d375/image.png" alt=""></p>
<p>이게 좀 많이 짜증났던건데 이 신청서가 어떻게 진행이 되고 있는건지 이 메일을 받기 전까지는 전혀 알 수 가 없다. 그저 기다려야만 한다.</p>
<p>이렇게 메일을 받게 되어서 앱 심사 페이지로 돌아가보면 
<strong>수익화 &gt; 가격 및 사용 가능여부 &gt; (페이지 하단) 앱 배포 방법</strong> 을 확인해보면 공개, 비공개가 아닌 이미지와 같이 링크가 하나 있는것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/008d427f-6130-4fac-81e4-4471e70589ac/image.png" alt=""></p>
<p>이제 이 링크를 통해서 다른 임직원 분들에게 배포를 할 수 있게 되는 것이다.</p>
<hr>
<h2 id="재심사">재심사</h2>
<p>그럼 이제 부푼 마음을 누르며 2번째 리젝에 회신을 넣어준다. &quot;나 잘 통과 되었다고 메일 받았으니 이제 통과 시켜줘~&quot;
<img src="https://velog.velcdn.com/images/sean_kk/post/19351223-66d3-4bd9-a43f-3b8e1b41f83e/image.png" alt=""></p>
<p>그럼 저쪽에서 심사(거의 프리패스 수준)를 지내고 나서 다음과 같은 회신이 오게 된다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/97923198-c364-407b-bd5c-37bce3c2e139/image.png" alt=""></p>
<p>그럼 뭐 이제 끝났다.</p>
<hr>
<h2 id="배포">배포</h2>
<p>심사가 잘 완료되면 그냥 일반 사용 앱 배포하는것과 같이 진행하면 된다.
<img src="https://velog.velcdn.com/images/sean_kk/post/e5c9f2cd-3f27-413c-af07-959493c004e2/image.png" alt=""></p>
<p><strong>이 버전 출시</strong> 버튼을 눌러서 스토어에 반영을 하고 위에 <strong>비공개 앱 신청서 승인</strong>에서 받은 URL 을 통해서 접근을 하면된다.</p>
<blockquote>
<p>근데 뭐 이건 다들 아시겠지만 이런거도 다 등록을 해줘야 한다. (웹에서 하라는대로만 하면 된다.)
<img src="https://velog.velcdn.com/images/sean_kk/post/b7c5b0fa-ca37-4d81-a190-54c83a6d6f34/image.png" alt="">
<img src="https://velog.velcdn.com/images/sean_kk/post/bffbb489-f94d-476e-9dce-b9b2865cd0ce/image.png" alt="">
<img src="https://velog.velcdn.com/images/sean_kk/post/f3d27c59-eeb8-4a65-852b-f52bbde497fe/image.png" alt=""></p>
</blockquote>
<p>이런거까지 싹 끝났다면 조금 시간이 지나서 제공된 링크로 접근을 해보면 이렇게 아주 잘 연결이 되는것을 알 수 있다.
<img src="https://velog.velcdn.com/images/sean_kk/post/c6510fff-f789-4cc7-b07e-fd7ac357922b/image.png" alt=""></p>
<hr>
<h1 id="마치며">마치며</h1>
<p>생각보다 뭔가 딱딱 정리된 자료를 잘 못찾아서 실제 해보면서 시간을 꽤 허비했고 이게 맞나..? 하는 의심이 많이 들었었다.</p>
<p>그러다보니 이거 나중에 또 언젠가 하게 될 수도 있을텐데 뭔가 기억이 안나거나 또 확신을 못가질 수 있을 거 같아서 이렇게 미래의 나를 위해서 그리고 이 글을 볼 누군가를 위해서 남겨둔다.</p>
<p>다들 화이팅 하십쇼!</p>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[배포] 애플 비공개 앱 배포를 진행하면서 1 - (다양한 애플의 앱 배포 방법)]]></title>
            <link>https://velog.io/@sean_kk/%EB%B0%B0%ED%8F%AC-%EC%95%A0%ED%94%8C-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%98%EB%A9%B4%EC%84%9C-1-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%95%A0%ED%94%8C%EC%9D%98-%EC%95%B1-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@sean_kk/%EB%B0%B0%ED%8F%AC-%EC%95%A0%ED%94%8C-%EB%B9%84%EA%B3%B5%EA%B0%9C-%EC%95%B1-%EB%B0%B0%ED%8F%AC%EB%A5%BC-%EC%A7%84%ED%96%89%ED%95%98%EB%A9%B4%EC%84%9C-1-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%95%A0%ED%94%8C%EC%9D%98-%EC%95%B1-%EB%B0%B0%ED%8F%AC-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 18 Dec 2025 06:16:39 GMT</pubDate>
            <description><![CDATA[<p>블로그</p>
<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--직접-경험">참고자료 : 직접 경험</h4>
</blockquote>
<h1 id="배경">배경</h1>
<p>회사에서 최근에 맡게 된 프로젝트로 앱을 만들게 되었는데 회사 규모가 그리 크지 않다보니 iOS / Android 에 많은 인원을 투입할 수 없어서 각각 1명씩 담당자가 붙어서 앱을 개발하게 되었다.</p>
<p>진행하는 프로젝트가 회사 내부의 인원들만 사용을 하는 회사 전용 앱이라 회사의 임/직원들만 사용하는것이라 배포 방식에 대한 고민을 안 할 수 가 없었다.
(애플은 이런 폐쇄적인 특정 환경에서만 적용되는 앱에 대해서 일반 배포로 심사를 올리면 Guideline 3.2 - Business 항목을 이유로 들면서 심사에서 거절이 난다.)</p>
<ul>
<li>이런식으로 리젝이 난다고 보면 된다.
<img src="https://velog.velcdn.com/images/sean_kk/post/ba3c4cc1-4cc6-4571-a3ea-f2761bdb2113/image.png" alt=""></li>
</ul>
<p>결과적으로는 <strong>비공개 앱 배포</strong>방식에 대해서 성공을 했고 이 방식대로 프로젝트를 진행하기로 하였다.</p>
<p>그럼 바로 본론으로 들어가기 전에 배포 방식의 종류는 어떤것들이 있나 확인해보려고 한다.</p>
<h1 id="배포-방식">배포 방식</h1>
<p>그전에 애플에서 진행하는 배포 방식에 대해서 하나하나 정리를 해 봐야 할 것 같다.</p>
<h3 id="1-일반-앱-배포">1. 일반 앱 배포</h3>
<p>우리가 흔히 아는 앱스토어 배포로 누구나 검색해서 다운로드를 받을 수 있다.</p>
<ul>
<li>특징: 설정한 국가의 <strong>모든 사용자</strong>에게 공개 된다.</li>
<li>문제점: 깐깐하기도 많이 깐깐하고 이런식으로 일반 대중이 사용할 수 없는 앱의 경우에는 심사 자체를 통과할 수 없다.</li>
<li>비고: 로그인 전에 체험하기 같은 기능을 억지로 넣는게 아니라면 지금 이런 비즈니스 3.2 위반을 넘길 수 없다.</li>
</ul>
<h3 id="2-ad-hoc배포">2. Ad Hoc배포</h3>
<p>개발자 계정에 기기의 고유 식별자(UDID)를 등록해서 배포하는 방법이다.</p>
<ul>
<li><p>특징: 앱스토어를 거치지 않아도 IPA를 <code>itms-services://?action=download-manifest&amp;~~</code> 와 같은 식의 링크로 앱을 설치 할 수 있다.</p>
</li>
<li><p>문제점</p>
</li>
</ul>
<ol>
<li><p><strong>UDID</strong> 등록 : 임/직원이 추가될 때 매번 해당 임직원의 UDID를 받아서 개발자 계정에 등록(이때 등록 시간이 24~72시간 정도 소요)을 해야한다.</p>
</li>
<li><p><strong>개발자 모드</strong> 설정 : iOS 개발자도 아닌 일반 개발자나 다른 부서의 인원이 아이폰의 이 설정을 켜두고 생활하는 경우가 몇이나 될거며 보안에도 좋지 않다.</p>
</li>
<li><p><strong>인원 제한</strong> : 1년에 100대까지만 등록이 가능하기에 만약에 회사의 임/직원이 100명이 넘는다면 내년에나 다른 인원이 사용 가능하다는 소리가 된다. (테스트 폰이 없는 경우에 한정)</p>
</li>
<li><p><strong>유효 기간</strong> : 1년마다 갱신해야 해서 그냥 하나 띡 만들어두고 우리 안바꿔! 가 되지 않는다는 소리이다.</p>
</li>
</ol>
<ul>
<li>결론 : 테스트로 적합한 방법이지 이게 배포 방식으로 사용하기에는 적합하지 않다.</li>
</ul>
<h3 id="3-abmapple-business-manager-앱-배포">3. ABM(Apple Business Manager) 앱 배포</h3>
<p>애플이 기업용으로 배포할 때 사용하는 권장하는 정석 방식으로 특정 기업의 ABM계정으로 앱을 비공개로 배포하는 방식이다.</p>
<ul>
<li>특징 : 앱스토어 노출 되지 않고 특정 기업에만 배포된다.</li>
<li>배포 방법 : 이 방식을 한다면 2번 리딤코드 방식을 많이 사용하는거 같은데 직접 사용은 안해봐서 뭐가 그나마 좋은지 모르겠다.</li>
</ul>
<ol>
<li>MDM : 회사 지급 폰에 원격 자동 설치</li>
<li><strong>리딤 코드</strong> : 엑셀에 (애플이 발급해주는) 코드를 받아서 임/직원들에게 나눠줘 배포를 진행하는 방식</li>
</ol>
<ul>
<li>문제점</li>
</ul>
<ol>
<li>MDM 시스템이 구축되어있지 않고 개인 폰을 사용한다면 불가</li>
<li>잦은 입퇴사가 있다면 매번 리딤코드를 생성하고 관리하는 관리포인트가 증가</li>
<li>이 ABM이 가능하게 하려면 이 관련 계정이 필요한데 이거 가입하는것도 며칠 단위로 걸리고 대표님한테 전화도 가고 좀... 복잡하고 귀찮다.</li>
</ol>
<ul>
<li><p>결론 : 규모가 크고 보안이 엄격하면 이게 맞지만 이런게 아니라면 굳이이다.</p>
<h3 id="4-비즈니스-계정">4. 비즈니스 계정</h3>
</li>
<li><p>애플 심사 없이 IPA를 직접 만들어 사내 서버나 MDM으로 직접 배포할 수있다.</p>
</li>
<li><p><strong>BUT</strong>, 이 유형으로 가입하기는 애플이 막다 수준으로 해서 중소기업에서 지금 시점에 새로 가입하는 건 어렵다고 보면 된다. 그러다보니 그냥 이건 머리에서 <strong>제외</strong>했다.</p>
</li>
</ul>
<h3 id="5-비공개-앱-배포">5. 비공개 앱 배포</h3>
<p>이번에 선택한 방식이고 찾아보니까 22년 부터 된다고 한다.
그래서 그런지 생각보다 정보가 많이 없어서 이게 맞나.. 저게 맞나.. 이러면서 직접 심사 넣고 하면서 직접 몸으로 방법을 깨우쳤다.</p>
<ul>
<li>특징</li>
</ul>
<ol>
<li>앱스토어에서 검색 불가</li>
<li><strong>고유 링크(URL)</strong>를 통해 접속하면 앱스토어 화면으로 가진다.</li>
<li>ABM 같이 복잡한 계정 연동이나 리딤 코드가 필요 없다.</li>
</ol>
<ul>
<li>장점 </li>
</ul>
<ol>
<li>관리의 용이성 : 별다른 사전 설정 없이 링크만 뿌리면 된다.</li>
<li>접근성 : 직원들 각자 개인 폰으로 링크만 누르면 된다.</li>
<li>심사 통과 : 사내앱이어도 애플이 인정해서 3.2 이슈에 대한 문제가 사라진다.</li>
</ol>
<ul>
<li>단점 : 만약 링크가 유출되면 외부인도 다운이 가능한데 이건 내부 로그인으로 막아두면 되는거라 큰 문제는 아니다.<h1 id="분량-조절-실패">분량 조절 실패</h1>
오..분량 조절 실패로 다음에 실제 해본것들을 작성해보겠다.</li>
</ul>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.
￼</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SPMS] 1. 인프라 구축의 서막 (고생의 시작) : 프롤로그]]></title>
            <link>https://velog.io/@sean_kk/%EA%B0%9C%EB%B0%9C-%ED%91%B8%EC%8B%9C%EC%86%94%EB%A3%A8%EC%85%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%ED%95%B4%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@sean_kk/%EA%B0%9C%EB%B0%9C-%ED%91%B8%EC%8B%9C%EC%86%94%EB%A3%A8%EC%85%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%ED%95%B4%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Mon, 08 Dec 2025 09:55:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--내-경험--ai">참고자료 : 내 경험 + AI</h4>
</blockquote>
<h1 id="갑자기-웬-솔루션">갑자기 웬 솔루션?</h1>
<p>사실 별 이유는 없다.
그저 한 번씩 오는 개발자들의 사이드 프로젝트 증후군 같은게 왔을 뿐이고 어떤 주제로 개발을 할까 고민을 했을뿐이다.</p>
<p>예전에 매번 혼자서 사이드 프로젝트를 할 때 로그인 만들고, 푸시 좀 붙여서 푸시 좀 받다가, 화면 좀 만들어보고 접는 프로젝트가 많았다.</p>
<p>이렇게 하나 만들다 접고 하는 과정을 할거면 모듈식으로 이런 기능들을 하나하나 미리 개발을 해두고 후에 다른 프로젝트에 그대로 붙이거나 해서 하면 되지 않을까?</p>
<p>라는 생각에서 시작하게 되었다.</p>
<h2 id="근데-왜-푸시">근데 왜 푸시?</h2>
<p>나는 iOS 개발자이다 보니 원래는 iOS에서 SwiftUI로 그리는 그래프 그림을 프레임워크로 개발해보고 싶었다.
그외의 토스트 메시지나 이디케이터 같은 UI적인 뭐 그런것들을 프레임워크로 묶어서 나만의 Package로 만들어볼 생각이었다.</p>
<p>근데 그러던 중에 프로젝트를 해보고 싶어졌고 서버도 사용하고 BO도 만들고 하는 뭐 그런걸 해보고 싶었을 뿐이었다.</p>
<p>그러다보니 어? 매번 골머리 썩던 푸시를 그냥 솔루션으로 만들어서 내 프로젝트 할 때 마다 구현하는게 아니라 그냥 솔루션으로 모든 내 사이드 프로젝트의 푸시를 하게 하면 되는거잖아? 싶어서 시작했다.</p>
<p><del>(뭐 그러다 사이드 프로젝트 수준에서 푸시 솔루션 쓰고 싶은 지인이 있다면 공유해도 상관 없는거니까</del>)~~</p>
<h2 id="작명">작명</h2>
<ul>
<li>예전에 어디서 본건데 프로젝트의 이름이 너무 멋드러지고 신경을 많이 쓰면 끝까지 잡고 있는게 힘들다고 했던 분이 계셨다.
뭐 나도 대충은 비슷한 생각이다. 이런 이름 신경쓰고 고민하고 이럴바에 일단 만들어 봐야 하니까.. 그래서 이름은 이렇게 짓기로 했다.</li>
</ul>
<blockquote>
<p>SPMS : Stein Push Message Service</p>
</blockquote>
<ul>
<li>Stein이 뭐냐고 묻는다면 그냥 내가 꾸린 개발 프로젝트 팀의 팀명 정도라고 생각하면 된다. (<del>팀장: 나, 팀원: 1명(나)</del>)</li>
</ul>
<hr>
<h1 id="기술-분석">기술 분석</h1>
<p>당연하게도 프로젝트를 시작하기 전에 무슨 기술을 써서 어느 수준까지 만들어야 하는것에 대한 고민을 하는 부분부터 시작했다.</p>
<h3 id="1-server-개발">1. Server 개발</h3>
<ul>
<li><p>환경 : NAS Docker</p>
<ul>
<li>푸시 서버가 돌아갈 환경부터 정했다. AWS를 사용해서 본격적으로 해볼까 했는데 그거보다는 집에 남아있던 NAS를 활용해서 일단 해보고 그 이후에 변경하기로 했다.</li>
</ul>
</li>
<li><p>언어 : C# .NET 9.0</p>
<ul>
<li>.NET, Node, Spring 등 이 있었는데 결론은 종종 취미로 건들던 .NET으로 하기로 했다. 이왕 하는거 최신 버전으로 사용하기로 했고 10.0 이 나왔던데 25.11월에 나온 버전이라서 그건 아직 너무 이른거로 생각해서 9.0으로 정했다.</li>
</ul>
</li>
</ul>
<h3 id="2-office-개발">2. Office 개발</h3>
<ul>
<li>기술: React + Vite<ul>
<li>FO / BO 둘다 그렇게 크게 고난도의 기술이 필요한건 아니어서 잘 사용은 못하더라도 레퍼런스 많고 도와줄 지인이 많던 React로 하기로 했다. Vite는 요즘 좋다고(<del>잼미니가 추천해줌</del>) 해서 같이 추가 했다.</li>
</ul>
</li>
<li>언어: TypeScript<ul>
<li>JS, TS 딱히 좋아하지 않고 전문 분야도 아니지만 RN 개발을 하면서 TS 를 해본 경험이 있어서 이거로 하기로 결정했다.</li>
</ul>
</li>
</ul>
<h3 id="3-framework-개발">3. Framework 개발</h3>
<ul>
<li>기술: iOS(Swift, XCFramework), Android(예정)<ul>
<li>뭐 본업이다 보니 이거로 정했고 안드로이드 쪽은 아직은 어떻게 할지 생각은 안했는데 저거 또한 내가 할 생각이기는 한데 당장에 할 생각은 없다.</li>
</ul>
</li>
</ul>
<h3 id="4-middleware-개발">4. Middleware 개발</h3>
<ul>
<li><p>메세지 큐: RabbitMQ</p>
<ul>
<li>사실 이거 잘 모른다.
근데 이전에도 푸시 작업했었는데 대량 발송을 할 경우 트래픽이 몰리거나 해서 서버가 뻗는 경우가 있고 이럴때 직접 개발을 하면 어려웠던걸로 기억을 한다.
그런데 이걸 사용하면 큐를 사용해서 안정적으로 처리하기가 쉽다고 해서 사용했다. (kafka 도 있었는데 이건 NAS Docker에서 하기에는 너무 무거움..)</li>
</ul>
</li>
<li><p>DB: MariaDB</p>
<ul>
<li>이거도 고른 이유는 이전에 한 번 다뤄본 경험이 있고 실무에서도 이걸 사용해서 하는걸 많이 보기도 했고 MS SQL 서버 같은 것은 .NET 과 잘 어울릴수 있지만 리소스를 많이 먹어서 제외했으며 마리아는 다른걸 다 떠나서 일단 가볍다</li>
</ul>
</li>
</ul>
<h3 id="5-devops-개발">5. DevOps 개발</h3>
<p>데브옵스라고 해도 될지는 모르겠지만 일단 체계는 갖춰야지.. 라는 생각에 해본것이다.</p>
<ul>
<li><p>SCM: Gitea</p>
<ul>
<li>GitHub 이 있는데 왜 Gitea를 했냐고 물어본다면 내 자료의 모든걸 다 내가 관리하고 소유하는 그런 생각을 가지고 있었을 뿐이다.
그럼 깃랩은 왜 안했냐 라고 한다면 걔는 무거워서 램을 많이 잡아 먹기에 제한적인 상황에서는 깃티가 좋아보여서 깃티로 진행했다.</li>
</ul>
</li>
<li><p>CI/CD: Jenkins</p>
<ul>
<li>로컬에서 소스를 수정하고 깃에 올려서 소스 관리를 하면서 NAS에는 따로 수동으로 올리고 하는건 너무 비효율적이라 생각했다.
그렇기에 깃에 브랜치에 맞게 푸시하면 알아서 젠킨스를 통해 NAS에 코드 복사하고 빌드하고 배포까지 하게 자동화 라인을 구축했다.</li>
</ul>
</li>
</ul>
<h1 id="일단-구축은-완료">일단 구축은 완료</h1>
<ul>
<li>뭐 뒤 Part에서 구축하면서 겪게 된 시행착오들을 보여주겠지만 아래 화면처럼 아주 잘 빌드가 된 모습을 볼 수 있다.
<img src="https://velog.velcdn.com/images/sean_kk/post/7b08de27-8f82-438b-b93c-65e598ec8c17/image.png" alt=""></li>
</ul>
<h1 id="참고자료">참고자료</h1>
<ul>
<li>구글의 어느 멋진 개발자분들의 블로그 </li>
<li>구글의 잼미니</li>
<li>이전에 해왔던 내 머리속 어딘가 있는 지식</li>
</ul>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 공식문서 학습하기 5 : ~형식시스템 / 레코드]]></title>
            <link>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-5-%ED%98%95%EC%8B%9D%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%A0%88%EC%BD%94%EB%93%9C</link>
            <guid>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-5-%ED%98%95%EC%8B%9D%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%A0%88%EC%BD%94%EB%93%9C</guid>
            <pubDate>Fri, 20 Sep 2024 01:20:39 GMT</pubDate>
            <description><![CDATA[<p>참고 자료: <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/tour-of-csharp/">MS Document</a></p>
<blockquote>
<ul>
<li>C#이 생각보다 자료가 많이 없어서 그냥 공식문서 보고 정리하려고 한다.
일단은 <code>공식문서</code>상의 구조를 따라가려고 하는데 내용은 내 맘대로 써질 예정</li>
<li>본질이 iOS 개발자라 많이 비교하면서 학습을 진행할 예정이다.<ul>
<li>@&gt;--- 이런 기호와 함께 &#39;기울임&#39;, &#39;작게&#39; 표시 되면 개인적인 생각이다.</li>
</ul>
</li>
<li>당연 이건 다른 언어를 했던 사람들이 C# 공부시 그나마 조금 편하라고 만들어 보는거지 아예 코드 짜는 사람이 처음이라면 이해 안될 수 있는다.    </li>
<li>이해 안되는건 뒤에 나올 내용이 앞에 나와서 그러는거니까 첨보는 단어면 일단 넘어가면 뒤에 다시 나온다.</li>
</ul>
</blockquote>
<h2 id="형식-시스템--레코드">형식 시스템 / 레코드</h2>
<p><small><em>@&gt;--- 일단 레코드라는건 swift에서 들어보지 못한 타입이다. 그러므로 내용을 보며 비슷한걸 찾아보겠다.</em></small></p>
<ul>
<li>레코드는 데이터 모델 작업에 <code>특별한</code>구문과 동작을 제공하는 클래스 <code>또는</code> 구조체 이다.</li>
<li><code>record</code> 한정자는 주 역할이 데이터를 저장하는 형식에 유용한 멤버를 합성하게 한다.</li>
</ul>
<h3 id="레코드-사용하는-경우">레코드 사용하는 경우</h3>
<ul>
<li>권고사항 (다음의 상황에서는 클래스나 구조체보다 레코드 사용이 좋다.)<br>1] <code>값 같음</code> 여부에 따라 달라지는 데이터 모델을 정의하려는 경우
2] <code>변경할 수 없는</code> 개체의 형식을 정의하려고 할때</li>
</ul>
<h3 id="값-같음">값 같음?</h3>
<ul>
<li>레코드의 경우에 <code>값 같음</code>은 <code>형식이 일치</code>하고 <code>모든</code>속성 및 필드 <code>값</code>이 동일하다고 비교되는 레코드 형식의 <code>두 변수가 같다는 것</code>을 의미한다.<ul>
<li>클래스 같은 <code>참조 타입들</code>의 경우 <code>같음</code>에서 <code>값 같음</code>이 구현되지 않는 한 기본적으로 <code>참조 같음</code>을 의미한다.<ul>
<li>즉, 클래스 형식의 두 변수는 같은 개체를 <code>참조</code>하는 경우 같다.</li>
<li>두 레코드 인스턴스의 같음을 확인하는 메서드와 연산자에서 값 같음을 사용한다.</li>
</ul>
</li>
</ul>
</li>
<li>일부 데이터 모델은 값 같음을 사용하지 않는다.<ul>
<li>예를 들어 Entity Framework Core(이하 EFC)는 참조 같음을 사용해 개념적으로 하나의 엔티티에 해당하는 엔티티형식의 인스턴스를 하나만 사용하는지 확인한다.<ul>
<li>값 같음을 사용하지 않기에 레코드 형식을 사용하기에는 적절치 않다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="불변성">불변성</h3>
<ul>
<li><code>변경할 수 없는 형식</code>은 인스턴스화 된 후 개체의 속성 또는 필드 값을 <code>변경</code>하는 것을 <code>방지</code>하는 형식이다.</li>
<li>불변성은 타입이 스레드로부터 안전해야 하거나 해시 테이블에서 동일하게 남아 있는 해시코드를 사용하는 경우에 <code>유용할 수</code> 있다.<ul>
<li>레코드는 변경할 수 없는 형식을 만들고 사용하기 위한 간결한 구문을 제공한다.</li>
</ul>
</li>
<li>불변성은 모든 곳에 다 적합하지 않는다. 위에서 예를 든 EFC는 변경할 수 없는 엔티티 형식을 사용한 업데이트를 지원하지 않는다.</li>
</ul>
<h3 id="레코드가-클래스-및-구조체와-다른-방식">레코드가 클래스 및 구조체와 다른 방식</h3>
<ul>
<li><p>클래스 또는 구조체 선언하고 인스터스화하는 동일 구문을 레코드와 함께 사용 가능하다.</p>
</li>
<li><p>class와 struct 키워드를 <code>record</code>와 <code>record struct</code> 로 대체해 사용한다.</p>
</li>
<li><p>상속을 표현하기 위한 구문은 레코드 클래스에서 <code>동일</code>하게 지원한다.</p>
</li>
<li><p>레코드가 클래스와 <code>다른</code>점은 다음과 같다.<br>1] 기본 생성자에서 위치 매개 변수 사용해 변경할 수 없는 속성을 사용해 타입을 만들고 인스턴스화할 수 있다.</p>
<details>
<summary> <em>설명</em> </summary>

<blockquote>
<p>  이게 무슨말일까?</p>
<ul>
<li><p>일단 레코드는 <code>불변</code> 데이터 모델을 쉽게 정의하기 위한 타입이다</p>
</li>
<li><p>클래스나 구조체와 달리 레코드는 기본 생성자를 지원하는데 이 생성자에서 <code>위치 매개 변수</code>를 정의하면, 해당 매개 변수들은 자동으로 <code>읽기 전용</code>속성으로 선언된다.</p>
<ul>
<li>위치 매개 변수 =&gt; 클래스 뒤에 입력하는 매개변수 를 의미 -&gt; 예시 코드에서 FirstName, LastName을 의미<pre><code class="language-C#">public record Person(string FirstName, string LastName);
</code></pre>
</li>
</ul>
<p>static void Main(string[] args)
{
  Person gildong = new Person(&quot;길동&quot;, &quot;홍&quot;);
}
```</p>
</li>
<li><p>이렇게 main에서 한거처럼 매개 변수를 통해 해당 속성들에 값을 할당하면 이후에는 값을 변경 할 수 없다.</p>
<blockquote>
<p>  근데 이게 왜??</p>
<ul>
<li>일단 클래스는 기본적으로 이런 방식의 간결한 정의가 불가능!</li>
<li>불변 속성을 가진 클래스를 만들려면 조금 코드가 많이 길어짐..<pre><code class="language-C#">public class Animal
{
  public string Name { get; }
  public Animal(string name)
  {
      this.Name = name;
  }
}</code></pre>
</li>
<li>코드가 길어지는건 물론이고, 불변성 유지 위한 추가 작업이 필요</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
</details>

<p>2] 클래스에서 <code>참조</code> 같음 또는 같지 않음을 나타내는 동일한 메서드와 연산자가 레코드에서 <code>값</code> 같음 또는 같지 않음을 나타낸다.</p>
<ul>
<li>예: == 및 Object.Eauals(Object)</li>
</ul>
<p>3] <code>with</code> 식을 사용해 선택된 속성에 새 값을 포함하는 변경할 수 없는 개체의 복사본을 만들 수 있다.</p>
<details>
<summary><em>설명</em></summary>

<blockquote>
<p>with 식?</p>
<ul>
<li><p>레코드와 함꼐 도입되었으므로 레코드와 함꼐 보는게 맞음</p>
</li>
<li><p>기존 객체를 기반으로 <code>일부</code> 속성만 변경해 <code>새로운</code> 객체를 <code>생성</code>시에 사용 </p>
<br>
</li>
<li><p>with 식은 기존 객체의 값을 <code>복사</code>해 새 객체를 생성하면서, 특성 속성의 값만 <code>변경</code> 할 수 있도록 해주는 구문</p>
<ul>
<li>이를 통해 <code>비파괴적 복사</code>가 가능<blockquote>
<p>비파괴적 복사??</p>
<ul>
<li>원본 객체를 변경하지 않고, 그 객체를 기반으로 새 객체를 생성하는것을 의미<ul>
<li><small><strong><em>얕은 복사랑 다른게 없군</em></strong></small></li>
<li>필요한 변경 사항만 반영된 새 객체를 만들다 보니 <code>불변</code> 객체를 다루거나 <code>데이터 무결성 유지</code>해야 하는 상황에서 중요<pre><code class="language-C#">Person gamdong = gildong with { FirstName = &quot;감동&quot; };</code></pre>
</li>
</ul>
</li>
<li>장점<ol>
<li>코드의 간결성</li>
<li>안정성 : 원본 변경않으므로 사이드 이펙트 방지</li>
<li>유지보수성 : 상태 변이에 따른 버그 발생 가능성을 줄여줌</li>
</ol>
</li>
</ul>
</blockquote>
</li>
</ul>
<blockquote>
<p>얕은 복사 &amp; 깊은 복사 (shallow copy &amp; deep copy)</p>
<ul>
<li>얕복: 객체의 <code>필드 값</code>을 그대로 복사, 주소는 동일 주소를 가짐<ul>
<li><code>with</code> 가 여기에 해당 : 만약 참조 타입 속성의 내부를 변경하면 원본과 복사본 둘다 영향 받을 수 있음</li>
</ul>
</li>
<li>깊복: 객체의 <code>필드 값</code>, <code>주소</code>가 가리키는 객체까지 <code>모두 새로</code>복사</li>
</ul>
</blockquote>
<blockquote>
<p>클래스에서도 비파괴적 복사 구현</p>
<ul>
<li>클래스에서도 가능하게 할 수 있는데 <code>복제</code>메서드와 <code>init</code> 접근자를 사용하면 된다.<pre><code class="language-C#">static void Main(string[] args)
{
  Animal dog = new Animal(&quot;Puppy&quot;);
  var dog2 = dog.With(p =&gt; p.Name = &quot;dog&quot;);
}
</code></pre>
</li>
</ul>
<p>public class Animal
{</p>
<pre><code>public string Name { get; set; }
public Animal(string name) =&gt; (Name) = (name);

public Animal With(Action&lt;Animal&gt; updater)
{
    var clone = (Animal)this.MemberwiseClone();
    updater(clone);
    return clone;
}</code></pre><p>}</p>
<pre><code>- 이런식으로 하면 얼추 비슷한 동작을 하는걸 클래스에서도 만들 수 있다.</code></pre></blockquote>
<blockquote>
<p>복제 메서드?</p>
<ul>
<li>현재 객테의 상태를 복사해 새 객체를 생성하는 메서드</li>
<li>이를 통해 원복 객체를 변경치 않고 일부 속성만 수정된 새로운 객체를 생성 가능함. (위에서 이야기 하던 with 랑 비슷하네)</li>
</ul>
<p>목적: with식이랑 흡사한 목적</p>
<ul>
<li>비파괴적 복사(얕복)</li>
<li>객체 복사</li>
<li>변형된 객체 생성</li>
</ul>
<p>방법: 클래스에서도 비파괴적 복사 구현의 코드 참고</p>
<ul>
<li><code>[자기 클래스] [메서드명] (매개변수)</code>로 구현</li>
<li><code>MemberwiseClone()</code> 이라는 .NET의 제공 메서드를 사용<ul>
<li>현 객체의 얕복본을 생성</li>
</ul>
</li>
<li>내부에 다른 값들을 넣고 다른 방식으로 하고 싶으면 해당 메서드를 알아서 잘 구현 하면 된다.</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
</details>

<p>4] 레코드의 <code>ToString</code> 메서드는 개체 형식 이름과 모든 퍼블릭 속성의 이름과 값을 표시하는 형식 문자열을 만든다</p>
<p>5] 레코드는 <code>다른 레코드</code>에서 <code>상속</code>될 수 있다.</p>
<ul>
<li>레코드 &lt; - &gt; 클래스 : 서로가 서로를 상속할 수는 <code>없다</code> <br></li>
</ul>
</li>
</ul>
<hr>
<ul>
<li><p>레코드 구조체는 <code>컴파일러</code>가 <code>같음(==)</code>과 <code>ToString을 위한 메서드를 합성</code>한다는 점에서 구조체와 다르다.</p>
<ul>
<li>값 기반의 동등성을 지원하며 불면 데이터 모델 정의에 유용하다.</li>
</ul>
</li>
<li><p>레코드 구조체 와 구조체의 차이점<br>1] 동등성 비교</p>
<ul>
<li>구조체<ul>
<li>기본적으로 구조체는 값 타입이기에 <code>모든</code>필드의 값을 비교해 동등성을 판단</li>
<li>그러나 Equals(), GetHashCode() 메서드 및 ==, !=  연산자를 자동으로 오버라이드 하지 않기에 필요에 따라 직접 오버라이드해서 동등성 비교를 구현해야 한다.</li>
</ul>
</li>
<li>레코드 구조체<ul>
<li>컴파일러가 자동으로 메서드나 연산자를 생성한다.</li>
<li>모든 필드의 값을 기반으로 동등성을 비교하도록 구현된다.<ul>
<li>즉 별도의 구현 없이 값 기반 동등비교가 가능</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>2] ToString 메서드</p>
<ul>
<li><p>구조체</p>
<ul>
<li>기본적으로 해당 메서드는 형식의 <code>전체 이름</code>을 반환한다.</li>
<li>필드의 값을 포함한 문자열을 얻으려면 해당 메서드를 오버라이드 해야 한다.</li>
</ul>
</li>
<li><p>레코드 구조체</p>
<ul>
<li>컴파일러가 <code>자동</code>으로 해당 메서드를 생성해 <code>형식 이름</code>과 <code>모든 퍼블릭</code>필드 또는 속성의 <code>이름</code>과 <code>값</code>을 포함한 문자열을 반환한다.<pre><code class="language-C#">public struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) =&gt; (X, Y) = (x, y);
}
</code></pre>
</li>
</ul>
<p>public record struct PointXY(int X, int Y);</p>
<p>static void Main(string[] args) {
  var p1 = new Point(1, 2);
  var p3 = new PointXY(3, 4);
  Console.WriteLine(p1.ToString());
  Console.WriteLine(p3.ToString());
}</p>
<p>// 결과 ----
// LearnMSDoc.Point
// PointXY { X = 3, Y = 4 }</p>
<pre><code></code></pre></li>
</ul>
<blockquote>
<p>컴파일러가 메서드를 합성한다?</p>
<ul>
<li>컴파일러가 해당 메서드의 코드를 자동으로 생성해준다는 의미로 개발자가 직접 코드를 짤 필요가 없다는 소리이다.</li>
<li>코드의 간결성, 일관성 있는동작, 불변 데이터 모델링에 적합한 동작을 만들어준다.</li>
</ul>
</blockquote>
</li>
<li><p>컴파일러는 위치 레코드 구조체에 대한 <code>Deconstruct</code>메서드를 합성한다.</p>
<blockquote>
<p>위치 레코드 구조?</p>
<ul>
<li>record struct 를 말하는거 같음</li>
<li>C# 에서 구조체와 레코드의 특성을 결합한 타입으로 <code>값</code>타입이며 <code>불변성</code>과 <code>값 기반 동등성</code>을 지원한다.</li>
<li>매개변수를 사용해 정의되며 컴파일러가 여러 유용한 메서드와 속성을 자동으로 생성해준다.</li>
</ul>
</blockquote>
<blockquote>
<p>Deconstruct 메서드? 
<small><em>@&gt;--- record sturct 선언하며 매개변수만 넣을 때 그게 잘 붙는지 그거 해주는거 같음</em></small></p>
<ul>
<li>객체의 필드를 개별 변수로 <code>분해</code>할 수 있게 해주는 메서드이다.</li>
<li>이 메서드를 사용하면 튜플 분해 구문을 통해 객체의 속성 값을 손쉽게 추출할 수 있다.<pre><code class="language-C#">public void Deconstruct(out int X, out int Y)
{
  X = this.X;
  Y = this.Y;
}</code></pre>
</li>
<li>일반 구조체에서는 해당 메서드가 자동으로 생성되지 않기에 수동으로 구현해야 한다.</li>
<li>위치 레코드 구조체의 장점으로는 <code>자동 생성된 멤버</code>, <code>불변성 지원</code>, <code>코드 간결성</code> 이 있다.</li>
</ul>
</blockquote>
</li>
</ul>
<hr>
<ul>
<li><p>컴파일러는 <code>record class</code>의 각 기본 생성자 매개 변수에 대한 공개 <code>초기화</code> 전용 속성을 합성한다.</p>
<ul>
<li><p>레코드 클래스에서 기본 생성자를 정의하면, 컴파일러는 각 매개 변수에 대응하는 <code>공개 init 전용 속성</code>을 자동으로 생성한다.</p>
</li>
<li><p><code>읽기 전용</code>이며 객체 <code>init</code>또는 <code>with</code>식을 통해서만 값을 설정할 수 있다.</p>
<pre><code class="language-C#">public record class Person(string FirstName, string LastName);

public string FirstName { get; init; }
public string LastName { get; init; }</code></pre>
</li>
</ul>
</li>
<li><p><code>record struct</code> 에서 컴파일러는 공개 읽기/쓰기 속성을 합성한다.</p>
<ul>
<li><p>기본 생성자(위치 매개 변수)를 정의하면, 컴파일러는 각 매개 변수에 대응하는 공개 읽기/쓰기 속성을 자동으로 생성한다.</p>
<pre><code class="language-C#">public record struct Point(int X, int Y);

public int X { get; set; }
public int Y { get; set; }</code></pre>
</li>
</ul>
</li>
<li><p>컴파일러는 <code>record</code> 한정자를 포함하지 않는 <code>class</code> 및 <code>struct</code> 형식에서 기본 생성자 매개 변수에 대한 속성을 만들지 않는다.</p>
<ul>
<li>레코드 한정자가 없는 일반에서는 <code>기본 생성자(위치 매개 변수)</code>를 사용할 수 없다.</li>
<li>따라서 컴파일러는 <code>기본 생성자 매개 변수에 대한 속성을 생성 않는다.</code></li>
<li>일반 클래스나 구조체에서 생성자 내부에서 수동으로 필드나 속성을 정의하고 초기화해야 한다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 공식문서 학습하기 4 : ~형식시스템 / 클래스]]></title>
            <link>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-4-%ED%98%95%EC%8B%9D%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-4-%ED%98%95%EC%8B%9D%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Fri, 20 Sep 2024 01:11:57 GMT</pubDate>
            <description><![CDATA[<p>참고 자료: <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/tour-of-csharp/">MS Document</a></p>
<blockquote>
<ul>
<li>C#이 생각보다 자료가 많이 없어서 그냥 공식문서 보고 정리하려고 한다.
일단은 <code>공식문서</code>상의 구조를 따라가려고 하는데 내용은 내 맘대로 써질 예정</li>
<li>본질이 iOS 개발자라 많이 비교하면서 학습을 진행할 예정이다.<ul>
<li>@&gt;--- 이런 기호와 함께 &#39;기울임&#39;, &#39;작게&#39; 표시 되면 개인적인 생각이다.</li>
</ul>
</li>
<li>당연 이건 다른 언어를 했던 사람들이 C# 공부시 그나마 조금 편하라고 만들어 보는거지 아예 코드 짜는 사람이 처음이라면 이해 안될 수 있는다.    </li>
<li>이해 안되는건 뒤에 나올 내용이 앞에 나와서 그러는거니까 첨보는 단어면 일단 넘어가면 뒤에 다시 나온다.</li>
</ul>
</blockquote>
<h2 id="형식시스템--클래스">형식시스템 / 클래스</h2>
<h3 id="참조형식">참조형식</h3>
<ul>
<li>클래스로 정의된 형식은 <code>참조</code> 형식이다.</li>
<li>런타임에 참조 형식의 변수 선언할 때 변수는 <code>new 연산자</code>를 사용해 클래스의 인스턴스를 명시적으로 만들거나, 다른 곳에서 생성되었을 수 있는 호환되는 형식의 개체를 <code>할당할 때까지</code> null을 포함한다.<ul>
<li><strong><em>= new로 인스턴스 생성하고 값 안 넣으면 null이다.</em></strong></li>
</ul>
</li>
<li>개체가 만들어지면 해당 개체에 대해 관리되는 힙에 메모리가 할당되고 변수에는 개체 위치에 대한 참조만 포함된다.</li>
</ul>
<h3 id="생성자-및-초기화">생성자 및 초기화</h3>
<ul>
<li><p>인스턴스를 만들 떄 해당 필드와 속성이 유용한 값으로 초기화하는 방법에는 여러 가지가 있다.</p>
<ul>
<li>기본값 설정</li>
<li>필드 init</li>
<li>생성자 매개 변수</li>
<li>개체 init</li>
</ul>
</li>
<li><p>모든 .NET 형식에는 디폴트가 있는데 숫자 형식은 <code>0</code>이고 모든 참조 형식은 <code>null</code>이다.</p>
</li>
<li><p>기본값이 올바른 값이 아닌 경우 초기값을 설정 할 수 있다.</p>
<pre><code class="language-C#">public class Container
{
    private int _capacity = 10;
}</code></pre>
</li>
<li><p>호출자가 초기 값을 설정하는 생성자를 정의해 초기 값을 제공하게 요구할 수 있다.</p>
<pre><code class="language-C#">public class Container
{
    private int _capacity;

    public Container(int capacity) =&gt; _capacity = capacity;
}</code></pre>
<blockquote>
<p>=&gt; : 이게 뭘까?</p>
<ul>
<li>해당 연산자는 <code>람다(Lamda)식</code>에서 사용되는 <code>람다 연산자</code> 또는 <code>화살표 연산자</code>라고 불린다.</li>
<li><code>람다 식</code>과 <code>본문 멤버</code> 를 정의할 때 사용한다.</li>
</ul>
<p>1 . 람다식에서의 사용: 람다 연산자
람다식은 <code>익명 함수</code>를 표현하는 간결한 방법으로 메서드나 대라지를 정의하지 않고도 함수를 전달할 수 있다.</p>
<ul>
<li>사용방법은 <code>(입력 매개변수) =&gt; 식 또는 문장 블록</code> 이런식으로 하며 사용 방법은 <code>2가지</code>가 있다.<ul>
<li>이렇게 표기할때 입력 매개 변수가 없으면 <code>() =&gt; 식 또는 문장 블록</code>으로 표현하면 된다.<pre><code class="language-C#">static void Main(string[] args)
{
var nums = new List&lt;int&gt; { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List&lt;int&gt; search = nums.FindAll(x =&gt; x % 2 == 0);
foreach (int n in search)
    Console.WriteLine(n);
}</code></pre>
</li>
</ul>
</li>
<li>람다식은 뭔가 함수를 새로 지정해서 해당 동작을 하는 그런 로직을 짜기에는 코드가 길어져 바로바로 확인이 안되거나 진짜 간단 로직 + 1회성인걸 굳이 함수까지 만들 필요 없을때 쓰면 좋을것으로 보인다.    </li>
<li>해당 코드 작성은 조금 더 익숙해져야 충분히 가능하겠지만 그런게 아니라면 초콤 문법 상으로 헷갈릴 여지가 있어보인다.<pre><code class="language-C#">Func&lt;int,int,int&gt; check = (x,y) =&gt; { return x*y; };</code></pre>
<small><em>@&gt;--- 자주 이용할 것 같은 람다식 구현 방법</em></small></li>
</ul>
<p>2 . Expression-bodied member (식 본문 멤버(?))</p>
<ul>
<li>일단 예제 코드를 먼저 봐보자<pre><code class="language-C#">public static int Add(int x, int y) =&gt; x + y;</code></pre>
</li>
<li>뭔가 람다식과 비슷한 모습을 보이는것처럼 보이는데 엄밀히 말하면 <code>완전 다른</code> 개념이다.</li>
<li>이 문법은 메서드, 속성, 생성자, 소멸자 등을 간결하게 표현하기 위해 사용된다.</li>
<li>해당 문법은 메서드나 속성의 본문을 간단한 식(expression)으로 대체하는 문법입니다.     <ul>
<li>메서드의 <code>본문</code>이 <code>한 줄</code>로 간단히 표현될 때 =&gt;를 사용해 이를 간결하게 나타낼 수 있습니다.</li>
</ul>
</li>
</ul>
<p><small><em>@&gt;--- 이게 뭔뜻이냐면 얘는 메서드라는거다. 
그렇다는건, Main 함수 내에서 선언하고 바로 사용하고 메모리 날리고가 안된다는 소리다.</em></small></p>
<p>정리</p>
<ol>
<li>람다식 = 익명 함수 정의에 사용 + 주로 <strong>대리자(delegate)</strong>나 LINQ에서 함수형 프로그래밍을 지원하기 위해 활용</li>
<li>Expression-bodied member = 간결하게 표현하는 방법으로 주로 코드의 가독성을 높이고 간단한 메서드를 축약할 때 사용
<small><em>@&gt;--- 약간 swift와 obj-C의 클로저나 블록과도 비슷한 느낌으로 보면 이해하기 쉬울것으로 보인다.</em></small></li>
</ol>
</blockquote>
<blockquote>
<p>클래스 생성과 동시에 초기화 시키기</p>
<ul>
<li>일단 예제 코드를 먼저 작성해보자<pre><code class="language-C#">class Program
{
  static void Main(string[] args)
  {
       TestClass tcl = new TestClass();
       Console.WriteLine(tcl.ReadX());
  }
}
</code></pre>
</li>
</ul>
<p>class TestClass
{</p>
<pre><code>private int x;
public TestClass() =&gt; x = 3;
public int ReadX() =&gt; x;</code></pre><p>}</p>
<pre><code>- 여기서 눈여겨 볼 곳은 TestClas클래스의 `TestClass 메서드` 이다.
  - 사용하는 방법은 매우 간단하다. 몇가지 `조건`만 맞다면 애용하게 될 방법이다.    
  1] 클래스를 코드상에 입력할때는 그 값을 정하지 않는 즉, 동적인 변수가 필요로 할때 해당 변수의 값을 초기화 시킬떄
  2] 해당 클래스가 처음 인스턴스로 할당될 때 해당 값이 초기화 되었으면 할때 사용하면 된다.
 &lt;br&gt;
  - 위의 조건들 외에도 여러 조건이 있겠지만 그걸 떠나 그냥 클래스 초기화 할때 쓴다 생각하믄 된다.
  - 사용방법 또한 매우 쉬운데 `public 클래스_이름(매개변수) = {초기화 구문}` 이런식으로 클래스의 `이름과 똑같은` 메서드를 만들어 뒤에 초기화 구문을 넣어주면 된다.
  - 예시 코드에서는 직접값을 넣어주고 있지만, 클래스 이름 뒤의 매개변수 넣는곳에 매개변수를 삽입해서 클래스 외부에서 변수의 값을 지정해줄 수 도 있다.
  - 그리고 `C# 12`에서 나온건데 아래처럼 직접 클래스 명에서 변수 때려서 하는것도 있는데 개인적으로는 전자의 방법을 더 애용할 것 같다.
    ```C#
    class TestClass(int inx)
    {
        private int x = inX;
        public int ReadX() =&gt; x;
    }
    ```</code></pre></blockquote>
</li>
<li><p>속성에서 required 한정자를 사용하고 호출자가 개체 이니셜라이저를 사용하여 속성의 초기 값을 설정하도록 허용할 수도 있습니다.    </p>
<ul>
<li><strong><em>이 키워드를 사용해서 생성된 변수는! 무조건 초기화 작업을 해야 한다는것을 의미함 new 클래스() 하고 <code>뒤에 {}</code> 이렇게 해서!!</em></strong></li>
</ul>
</li>
<li><p>예시 코드는 다음과 같다.</p>
<pre><code class="language-C#">class Program
  {
      static void Main(string[] args)
      {
          TestClass tcl = new TestClass() { y = 3 };
      }
  }

class TestClass
{
    public required int y { get; set; }
}</code></pre>
</li>
</ul>
<h3 id="클래스-상속">클래스 상속</h3>
<ul>
<li>클래스는 OOP의 기본적인 특성인 <code>상속</code>을 완전히 지원한다.</li>
<li>클래스 생성 시 <code>sealed</code>로 정의디지 않은 다른 클래스에서 상속할 수 있다.</li>
<li>다른 클래스는 클래스에서 상속하고 클래스 가상 메서드를 재정의할 수 있으며 하나 이상의 인터페이스를 구현할 수 있다.</li>
<li>상속은 <code>파생</code>을 통해 수행된다.
<small><em>@&gt;--- 공식문서를 보다보면 상속에서 <code>기본</code>,<code>파생</code>을 나눠서 설명을 한다.
  <code>기본</code>의 경우에는 가장 윗단 즉 트리의 <code>루트 노드</code> 같은 <code>형식</code>을 이야기 한다.
  <code>파생</code>의 경우에는 기본을 상속하는 모든 <code>형식</code>들을 이야기 한다.
  그래서 어떤 <code>파생</code>은 무조건 자식이 아닐 수도 있다. (이 파생을 상속하는 다른 파생이 있으면 부모이자 자식이 되는거니까)
  </em></small><ul>
<li>즉, 데이터와 동작을 상속하는 소스 기본 클래스를 사용해 선언된다.</li>
<li>파생 클래스 이름 뒤에 콜론(:) 과 상속하는 기본 클래스 이름을 추가해 상속 기본 클래스를 지정한다.</li>
<li>기본 클래스의 초기화(<small><strong><em>&#39;본문에서는 기본 클래스가 포함된 경우&#39;라고 써져있다.</em></strong></small>) 생성자를 제외하고 모든 멤버를 상속한다.</li>
</ul>
</li>
<li>예시 코드는 다음과 같다.<pre><code class="language-C#">public class Sean: Person { ... }</code></pre>
</li>
<li>하나의 기본 클래스에서만 직접 상속할 수 있다.<ul>
<li>그러나 기본 클래스가 다른 클래스에서 상속될 수 있으므로 여러 클래스를 간접 상속 가능하다.</li>
</ul>
</li>
<li>클래스는 하나 이상의 인터페이스를 직접 구현할 수 있다.
<small><em>@&gt;--- abstract과 sealed 에 관한 부분은 이미 서술했다.</em></small></li>
<li>클래스 정의는 여러 소스 파일로 분할 될 수 있고 자세한 건 <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods">Partial 클래스 및 메서드 공식문서</a>를 참조하면 된다.</li>
</ul>
<blockquote>
<p>클래스의 상속과 인터페이스    </p>
<ul>
<li>swift와 다르게 C#에서의 클래스는 <code>하나의</code> 상속만 가능하기에 이를 조금이나마 따라하는 작업으로 인터페이스를 사용한다.</li>
<li>여러 인터페이스를 구현함으로 다중 상속의 <code>일부</code> 기능을 <code>흉내</code>낼 수 있다.   </li>
<li><strong>중요한 키워드는 <code>일부</code>, <code>흉내</code> 이다.*</strong></li>
</ul>
<blockquote>
<p>왜 일부 흉내일까?</p>
<ul>
<li><p>상속과 인터페이스로 구현했을때의 코드 차이를 예시로 보여주겠다.</p>
<pre><code class="language-C#">public class Animal
 {
     public void Eat()
     {
         Console.WriteLine(&quot;eating...&quot;);
     }
 }

 public class Dog : Animal
 {
     public void NextAction()
     {
         Eat();
         Console.WriteLine(&quot;next...&quot;);
     }
 }</code></pre>
<pre><code class="language-C#">public interface Bird
{
   void Fly();
}

public interface Carnivore
{
   void WantMeat();
}

public class Eagle : Animal, Bird, Carnivore
{
   public void Fly()
   {
       Console.WriteLine(&quot;flying...&quot;);
   }

   public void WantMeat()
   {
       Console.WriteLine(&quot;Want Meat...&quot;);
   }

   public void NextAction()
   {
       Fly();
       Eat();
       WantMeat();
       Console.WriteLine(&quot;next...&quot;);
   }
}</code></pre>
</li>
<li><p>상속은 애초에 부모의 모든것을 가져오기에 <code>부모가 구현해둔</code> 로직까지 다 가져올 수 있지만</p>
</li>
<li><p>인터페이스의 경우에는 그렇다기 보다는 그냥 <code>청사진</code>을 그려주는 역할밖에 못하기에 해당 로직은 선택한 클래스에서 구현을 해줘야 한다.</p>
</li>
<li><p>그렇기에 의존성이나 관계도 또는 코드의 이해를 위하는 방법은 둘이 비슷할지 언정 정작 내부 디테일적으로 보게 되면 완벽하게 다른걸 알 수 있다.</p>
</li>
</ul>
</blockquote>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 공식문서 학습하기 3 : ~형식시스템 / 네임스페이스]]></title>
            <link>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-3</link>
            <guid>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-3</guid>
            <pubDate>Fri, 20 Sep 2024 00:56:42 GMT</pubDate>
            <description><![CDATA[<p>참고 자료: <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/tour-of-csharp/">MS Document</a></p>
<blockquote>
<ul>
<li>C#이 생각보다 자료가 많이 없어서 그냥 공식문서 보고 정리하려고 한다.
일단은 <code>공식문서</code>상의 구조를 따라가려고 하는데 내용은 내 맘대로 써질 예정</li>
<li>본질이 iOS 개발자라 많이 비교하면서 학습을 진행할 예정이다.<ul>
<li>@&gt;--- 이런 기호와 함께 &#39;기울임&#39;, &#39;작게&#39; 표시 되면 개인적인 생각이다.</li>
</ul>
</li>
<li>당연 이건 다른 언어를 했던 사람들이 C# 공부시 그나마 조금 편하라고 만들어 보는거지 아예 코드 짜는 사람이 처음이라면 이해 안될 수 있는다.    </li>
<li>이해 안되는건 뒤에 나올 내용이 앞에 나와서 그러는거니까 첨보는 단어면 일단 넘어가면 뒤에 다시 나온다.</li>
</ul>
</blockquote>
<h2 id="형식시스템--네임스페이스">형식시스템 / 네임스페이스</h2>
<ul>
<li><p>.NET에서 사용하는걸 예시로 봐보자</p>
<pre><code class="language-C#">System.Console.WriteLine(&quot;Hello World!&quot;);

Console.WriteLine(&quot;Hello World!&quot;);</code></pre>
</li>
<li><p>System은 네임스페이스고 Console은 해당 네임스페이스의 클래스이다.</p>
</li>
<li><p>두번째 예시의 코드처럼 사용하고 싶다면 using 키워드를 사용해서 <code>using Systen;</code> 추가하면 된다.</p>
</li>
<li><p>고유한 네임스페이스를 선언하면 관리 측면에서 클래스 및 메서드 이름의 범위를 제어할 수 있다.</p>
</li>
<li><p>아래처럼 하면 된다.</p>
<pre><code class="language-C#">namespace AnotherSpace
{
    class AnotherClass
    {
        // ... 클래스 선언
    }
}</code></pre>
</li>
<li><p>using 키워드는 <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/using-directive">공식문서</a>를 참조할 수 있다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#] 공식문서 학습하기 2 : ~ 형식 시스템 / 개요]]></title>
            <link>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-2-%ED%98%95%EC%8B%9D-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@sean_kk/C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-2-%ED%98%95%EC%8B%9D-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Fri, 20 Sep 2024 00:53:49 GMT</pubDate>
            <description><![CDATA[<p>참고 자료: <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/tour-of-csharp/">MS Document</a></p>
<blockquote>
<ul>
<li>C#이 생각보다 자료가 많이 없어서 그냥 공식문서 보고 정리하려고 한다.
일단은 <code>공식문서</code>상의 구조를 따라가려고 하는데 내용은 내 맘대로 써질 예정</li>
<li>본질이 iOS 개발자라 많이 비교하면서 학습을 진행할 예정이다.<ul>
<li>@&gt;--- 이런 기호와 함께 &#39;기울임&#39;, &#39;작게&#39; 표시 되면 개인적인 생각이다.</li>
</ul>
</li>
<li>당연 이건 다른 언어를 했던 사람들이 C# 공부시 그나마 조금 편하라고 만들어 보는거지 아예 코드 짜는 사람이 처음이라면 이해 안될 수 있는다.    </li>
<li>이해 안되는건 뒤에 나올 내용이 앞에 나와서 그러는거니까 첨보는 단어면 일단 넘어가면 뒤에 다시 나온다.</li>
</ul>
</blockquote>
<h2 id="형식시스템--개요">형식시스템 / 개요</h2>
<ul>
<li>결국에는 <strong>Type</strong>들에 대해서 설명할 부분이다.<pre><code class="language-C#">int num = 5;
float fNum = 1.0f;
double dNum = 1.0;
bool flag = false;</code></pre>
</li>
</ul>
<br>

<h3 id="1-기본-제공-타입-종류">1. 기본 제공 타입 종류</h3>
<ul>
<li>값 형식 타입으로는 bool, int, float, double, char 같이 자주 쓰이는건 물론이고, byte, sbyte, decimal, uint, nint, lomg. ulong, short, ushort 같은 것들도 있다.</li>
</ul>
<ul>
<li>참조 형식 타입으로는 object, string, dynamic 이 존재 한다.</li>
</ul>
<br>

<h3 id="2-사용자가-직접-지정하는-타입">2. 사용자가 직접 지정하는 타입</h3>
<pre><code class="language-C#">struct, class, interface, enum, record</code></pre>
<ul>
<li>이런것들 뿐 아니라 타입 관련해서는 <a href="https://learn.microsoft.com/ko-kr/dotnet/standard/class-library-overview">.NET 클래스 라이브러리</a>를 참조하면 좋다</li>
</ul>
<blockquote>
<p>.NET 클래스 라이브러리</p>
<ol>
<li>명명 규칙</li>
</ol>
<ul>
<li>계층 구조를 의미하는 스키마 이름을 지정하는데 <code>. 구문</code>을 사용</li>
<li>이 방식을 사용하면 관련 형식을 네임스페이스로 그룹화해 보다 쉽게 검색, 참조 가능<br>System.Collections.Generic.List&lt; T &gt; 이런 구문이 있다면 System.Collections.Generic 네임스페이스에 속하는 List&lt; T &gt;의 형식을 나타내는것</li>
</ul>
<ol start="2">
<li>System 네임스페이스</li>
</ol>
<ul>
<li>위에서 타입들 나열해놨으니 그것들 확인해볼것</li>
</ul>
<p>3.데이터 구조체</p>
<ul>
<li>다양한 데이터 구조 집합이 포함되어있는데 대부분 <code>컬렉션</code>이지만 다른 형태도 존재<ol>
<li>Array : idx 로 접근 가능한 개체 배열 -&gt; 고정 적 크기 존재</li>
<li>List  : idx 로 접근 가능한 개체 목록 -&gt; 크기가 자동 조정</li>
<li>Dictionary&lt;TKey,TValue&gt; : 키를 통해 값에 엑세스 가능 -&gt; 크기 자동 조정</li>
<li>Uri   : 개체 표현을 제공하며 URI 부분에 쉽게 엑세스 가능</li>
<li>DateTime : 날짜와 시간으로 표시된 시간
<small><em>@&gt;--- 뭐 이렇게 있다고는 하는데 대게 List랑 Dictonary를 자주 사용할 것 같다.(동적 크기 변경 짱..)</em></small></li>
</ol>
</li>
</ul>
<ol start="4">
<li>유틸리티 API</li>
</ol>
<ul>
<li>여러 중요 작업 기능 제공하는 유틸리티 API 집합이 포함<ol>
<li>HttpClient : URI로 식별되는 리소스에서 HTTP 요청을 보내고 HTTP 응답을 받기 위함</li>
<li>XDocument  : XML 문서 로드, LINQ 사용해 쿼리하기 위함</li>
<li>StreamReader / StreamWriter : 파일 읽기/ 쓰기</li>
</ol>
</li>
</ul>
</blockquote>
<br>

<h3 id="3-cts-공용-형식-시스템">3. CTS (공용 형식 시스템)</h3>
<ul>
<li><p>.NET 타입에 대해서 <code>2가지</code> 기초 사항을 이해해야 함</p>
<ol>
<li><code>상속</code>원칙을 지원<ul>
<li>타입은 기본 타입에서 파생될 수 있고 기본 타입의 메서드, 속성 및 기타를 상속한다.</li>
<li>마찬가지로 기본 형식이 다른 형식에서 파생될 수 도 있음</li>
</ul>
</li>
<li>각 타입은 <code>값</code> 또는 <code>참조</code> 타입으로 정의<ul>
<li>이는 모든 사용자 지정 타입과 자체 사용자 정의 타입도 포함된다.</li>
<li>둘(값 과 참조)은 컴파일 시간 규칙 및 런타임 동작이 다르다.<blockquote>
<p>사용되는 모든 타입들은 모두 System이라는 네임스페이스에 구성되어있는데
타입들이 포함된 네임스페이스는 타입이 값인지 참조인지 관련은 없다.</p>
</blockquote>
</li>
</ul>
</li>
</ol>
</li>
<li><p>클래스(class) 및 구조체(struct)는 .NET CTS의 기본 구문 중 2가지인데, 각각은 기본적으로 하나의 논리 단위에 속하는 데이터 및 동작 집합을 캡슐화 하는 데이터 구조이다.</p>
</li>
<li><p>데이터와 동작은 class, struct 또는 record의 <code>멤버</code>다.</p>
<ul>
<li>멤버는 메서드, 속성, 이벤트 등을 포함한다.</li>
</ul>
</li>
<li><p>class, struct, record 선언은 런타임에 인스턴스 또는 개체를 만드는데 사용되는 청사진과도 같다.</p>
</li>
<li><p>Class 는 <code>참조</code>타입이다.</p>
<ul>
<li>이 타입은 개체가 만들어지면 개체가 할당되는 변수는 해당 메모리에 대한 참조만 보유한다.</li>
<li>개체 참조가 새 변수에 할당되면 새 변수는 원래 개체를 나타낸다.</li>
<li>모두 동일 한 데이터를 참조하므로 한 변수를 변경하면 다른 변수에도 반영된다.</li>
</ul>
</li>
<li><p>Struct 는 <code>값</code>타입이다.</p>
<ul>
<li>구조체가 만들어지면 해당 구조체가 할당되는 변수에 구조체의 <code>실제</code> 데이터가 포함된다.</li>
<li>구조체를 새 변수에 할당하면 구조체가 <code>복사</code>된다. </li>
<li>따라서 새 변수와 원래 변수에 동일한 데이터의 두가지 별도 복사본이 포함되므로, 한 복사본의 변경은 다른 복사본에 영향을 주지 않는다.</li>
</ul>
</li>
<li><p>Record 는 <code>값</code>일 수도 <code>참조</code>일 수도 있다.</p>
</li>
<li><p>일반적으로 Class가 좀 더 복잡한 동작 모델링에 사용된다.</p>
<ul>
<li>일반적으로 클래스가 만들어진 후 수정<code>할</code> 데이터를 저장한다.</li>
</ul>
</li>
<li><p>Struct는 작은 데이터 구조에 가장 적합하다.</p>
<ul>
<li>일반적으로 구조체가 만들어진 후 수정하지 <code>않을</code> 데이터를 저장한다.</li>
</ul>
</li>
</ul>
<br>

<h3 id="4-값-형식">4. 값 형식</h3>
<ul>
<li>값 형식에는 해당 값이 <code>직접</code> 포함된다.</li>
<li>구조체의 메모리는 변수가 선언된 컨텍스트에서 인라인으로 할당된다.</li>
<li>값 타입의 변수에 대한 별도의 힙 할당이나 가비지 수집 오버헤드는 없다.</li>
<li>record struct 형식을 선언할 수 있다.</li>
</ul>
<h4 id="struct--enum">struct &amp; enum</h4>
<ol>
<li>Struct</li>
</ol>
<ul>
<li><p>구조체는 System.ValueType에서만 상속할 수 있기에 &#39;사용자 정의 클래스 또는 구조체에서 상속하는 구조체&#39;를 정의 할 수 없다.</p>
</li>
<li><p>그러나, 구조체는 하나 이상의 <code>인터페이스</code>를 구현할 수 있다.</p>
<ul>
<li>구조체 형식을 구현하는 인터페이스 형식으로 캐스팅 할 수 있으며 이 캐스트로 인해 boxing 작업은 관리되는 힙의 참조 형식 개체 내에 구조체를 래핑</li>
</ul>
</li>
<li><p>일단 구조체는 맨 윗줄 이랑 값 타입이다! 라는게 중요함</p>
</li>
<li><p><code>struct</code>키워드를 사용해 고유한 사용자 지정 값 타입을 만들며, 일반적으로 구조체는 다음과 같이 소규모 관련 변수 집합의 컨테이너로 사용한다.</p>
<pre><code class="language-C#">  public struct Coords
  {
      public int x, y;

      public Coords(int x, int y)
      {
          this.x = x; 
          this.y = y;
      }
  }</code></pre>
</li>
<li><p><a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/builtin-types/struct">[구조체 공식문서]</a> 구조체의 공식 문서는 다음을 참조하면된다.
<small><em>@&gt;--- 후에 이 또한 작성할 예정</em></small></p>
</li>
</ul>
<ol start="2">
<li>enum (열거형)</li>
</ol>
<ul>
<li>열거형은 명명된 정수, 상수 집합을 정의한다.<pre><code class="language-C#">  public enum DBMode
  {
      Create = 1,
      Read,
      Update,
      Delete,
  }</code></pre>
</li>
<li>위와 같이 작성하면 되는데 열거형 작성 방법은 Swift에서 하는거랑 비스무리 함</li>
<li>그리고 구조체에 적용되는 모든 규칙이 열거형에도 적용이 되며 <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/builtin-types/enum">[열거형 공식문서]</a>를 참조 할 것</li>
</ul>
<h3 id="5-참조-형식">5. 참조 형식</h3>
<ul>
<li><p>class, record, delegate, 배열, interfacer 로 정의된 형식이다.</p>
</li>
<li><p>해당 참조 타입의 변수 선언시에는 해당 타입의 인스턴스를 할당하거나 new 연산자를 사용해 생성할 때까지 값 null을 포함한다.</p>
</li>
<li><p>참조 형식은 상속을 완벽하게 지원하는데 class 만들 떄 <code>sealed</code>로 정의되지 않은 기타 인터페이스 또는 클래스에 상속할 수 있다.
<small><em>@&gt;--- 이게 뭔소리일까.. 알아봄 (이해 안되면 일단 넘겨도 됨)***</em></small></p>
</li>
<li><p>클래스, 구조체, 레코드 <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/object-oriented/">공식문서</a> 는 여길 참고</p>
</li>
<li><p>상속은 <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/object-oriented/inheritance">공식문서</a> 여길 참고</p>
<blockquote>
<p><em>class 만들 떄 <code>sealed</code>로 정의되지 않은 기타 인터페이스 또는 클래스에 상속할 수 있다?</em><br><small><em>@&gt;--- 이게 무슨 소리일까 한 번 고민해봅시다.</em></small>    </p>
<ul>
<li>일단 참조 타입은 class, interface, delegate, array, string을 포함하며 참조형식은 힙에 저장되며, 변수는 해당 객체의 <code>메모리 주소</code>를 가진다.</li>
<li>참조 타입은 상속을 완벽하게 지원하며 C#의 경우에는 <code>단일 상속</code>을 지원한다. 즉, child 는 1 parent 만 상속할 수 있다. <ul>
<li>하지만, 인터페이스는 <code>다중 상속</code>이 가능하며, 틀래스는 여러개의 인터페이스를 <code>구현</code>할 수 있다.</li>
</ul>
</li>
<li>그래서 참조 타입은 상속을 통해 기능의 확장과 다형성을 구현할 수 있다.</li>
</ul>
<p>그럼 <code>sealed</code> 키워드는 뭘까?</p>
<ul>
<li><p>해당 키워드는 클래스 선언 앞에 사용되어, 해당 <code>클래스를 더 이상 상속할 수 없도록</code> 한다.</p>
</li>
<li><p>즉, 해당 키워드로 선언된 클래스는 <code>상속의 마지막 단계</code>로 다른 클래스가 이를 상속받아 확장할 수 없다.</p>
<pre><code class="language-C#">public sealed class FinalClass { }</code></pre>
<ul>
<li>근데 해당 키워드는 메서드나 속성 앞에서도 사용할 수 있으며, 이때는 상속 받은 클래스에서 해당 메서드를 재정의 할 수 없게 한다.</li>
<li>이때는 <code>override</code> 키워드와 함께 사용해야 한다.</li>
<li><code>virtual</code> 키워드는 해당 멤버가 child 클래스에서 override 될 수 있음을 나타낸다.<ul>
<li>즉 기본 클래스에서 선언된 멤버를 자식 클래스에서 재정의해 다형성을 구현할 수 있게 하는 중요한 키워드이다.</li>
<li>해당 키워드를 사용하는 멤버는 기본적으로 기본 클래스에서 <code>구현되어</code> 있어야 한다.</li>
<li>그냥 <code>public virtual void Show();</code> 이렇게 넘기면 안됨!!!</li>
</ul>
</li>
<li>구현 예시는 아래와 같다.<pre><code class="language-C#">class BaseClass
{
  public virtual void Show()
  {
      Console.WriteLine(&quot;Hello World&quot;);
  }
}
</code></pre>
</li>
</ul>
<p>class DerivedClass : BaseClass
{</p>
<pre><code>public sealed override void Show()
{
    Console.WriteLine(&quot;Hello SUB World&quot;);
}</code></pre><p>}</p>
<p>class SubDerivedClass: DerivedClass
{</p>
<pre><code>// Show 메서드 재정의 불가</code></pre><p>}</p>
<pre><code>&lt;br&gt;
</code></pre></li>
</ul>
<p>근데 여기서 override 키워드가 나온 김에 다른 키워드를 하나 더 보자면 <code>abstract</code>가 있다.</p>
<ul>
<li><p>이 키워드는 virtual 메서드와 비교를 해보면 된다.</p>
</li>
<li><p>virtual 키워드 같은 경우에는 <code>기본 클래스에서</code> 구현을 해야지 사용이 가능한데 이 키워드로 선언된 메서드는 기본 구현이 필요 없다.</p>
<ul>
<li>단, 이렇게 선언된 메서드의 경우 이 메서드의 클래스를 상속한 <code>자식</code> 클래스는 <code>반드시</code> 해당 메서드를 <code>구현</code>해야 한다.<ul>
<li>마치 인터페이스의 디폴트 구현 안된 메서드와 같은 기능같다.</li>
</ul>
</li>
</ul>
</li>
<li><p>구현 예시는 위에서 구현했던 코드들을 재활용해본다.</p>
<pre><code class="language-C#">public abstract class BaseClass
{
    public virtual void Show()
    {
        Console.WriteLine(&quot;Hello World&quot;);
    }
    public abstract void Log(string msg);
}

class DerivedClass : BaseClass
{
    public sealed override void Show()
    {
        Console.WriteLine(&quot;Hello SUB World&quot;);
    }
    public override void Log(string msg)
    {
        Console.WriteLine($&quot;{msg}&quot;);
    }
}

class SubDerivedClass: DerivedClass
{
    // Show 메서드 재정의 불가
}</code></pre>
</li>
</ul>
</blockquote>
</li>
</ul>
<h4 id="class">Class</h4>
<ul>
<li><p>클래스 선언
<small><em>@&gt;--- 당연 내부 설정 로직은 후에 기술할 예정</em></small></p>
<pre><code class="language-C#">class MyClass
{
    public int classNum2 = 2;
  public int classNum { set; get; }
}</code></pre>
</li>
<li><p>위와 같은 클래스가 있다고 가정했을 떄 이를 생성 및 할당하는 방법을 보자면 다음과 같다.</p>
<pre><code class="language-C#">  MyClass myClass = new MyClass();
  MyClass myClass2 = myClass;</code></pre>
</li>
</ul>
<h4 id="interface">Interface</h4>
<ul>
<li>interface는 new 연산자를 사용해 직접 인스턴스화할 수 <code>없다</code></li>
<li>대신 인터페이스를 구현하는 클래스의 인스턴스를 만들고 할당한다.</li>
<li>인터페이스는 구현해야 하는 메서드, 프로퍼티, 이벤트 등 멤버들의 계약(@&gt;--- contract 라고 하는데 제약이지 않을까..)을 정의하는 역할을 한다.<ul>
<li>이를 통해 코드의 유연성, 확장성, 재사용성을 높일 수 있다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>근데 이거 개념적으로는 swift에서 protocol이랑 비슷한거 같다.</p>
<ul>
<li>그렇게 생각하는 이유로는 인터페이스의 경우에는 하나 이상의 추상적인 요소들을 포함하는 <code>추상적</code>인 타입이라는 정의와</li>
<li>클래스나 구조체가 특정 기능을 구현하도록 <code>강제</code> 하고</li>
<li>인스턴스 할 수 없기에 인터페이스 <code>자체의 객체</code>를 생성할 수 <code>없고</code></li>
<li>모든 멤버들이 일단 public이며(<code>다 구현해야</code> 한다는 것), 구현부가 없다.<ul>
<li>근데 이거도 보니까 C# 8.0 부터는 기본 값을 넣을 수 있음</li>
<li>그렇다는거는 이걸 구현하는게 강제 된다는게 아님<ul>
<li>만약 <code>int Add(int a, int b);</code> 를 인터페이스로 넣어놨는데 만약 디폴트 구현을 안해놨으면 이 인터페이스를 사용하는 클래스에서 해당 함수들을 계속 구현해야 하는데..<br>디폴트로 <code>return a+b</code> 를 구현해두면 매번 구현 할 필요 없이 저 동작을 할 수 있게 된다. </li>
</ul>
</li>
</ul>
</li>
<li><code>다중</code> 상속이 되기에 클래스나 구조체는 <code>여러 개</code>의 인터페이스를 구현할 수 있다.    </li>
</ul>
<p>그런고로 Interface == Protocol 이라는 생각을 한다. </p>
<ul>
<li><p>구현은 다음과 같다.</p>
<pre><code class="language-C#"> public interface IMyInterface
 {
     void Log(string msg);
     void LogError(string error);
 }

 class InterfaceClass : IMyInterface
 {
     public void Log(string msg)
     {
         Console.WriteLine($&quot;{msg}&quot;);
     }

     public void LogError(string error)
     {
         Console.WriteLine($&quot;{error}&quot;);
     }
 }

 class Program
 {
     static void Main(string[] args)
     {
         IMyInterface myInterface = new InterfaceClass();
     }
 }</code></pre>
</li>
</ul>
</blockquote>
<ul>
<li>개체가 만들어지면 관리되는 힙에 메모리가 할당되고, 변수는 개체의 위치에 대한 참조만 포함한다.<ul>
<li>관리되는 힙의 형식은 할당될 때, 회수될 때 모두 오버헤드가 필요하다.</li>
<li><code>가비지 수집</code>은 회수를 수행하는 자동 메모리 관리 기능으로 고도로 최적화되고 대부분에서 성능 문제를 일으키지 않는다.<ul>
<li><a href="https://learn.microsoft.com/ko-kr/dotnet/standard/automatic-memory-management">자동 메모리 관리</a>에 대해서는 이걸 참고</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="배열">배열</h4>
<ul>
<li>모든 배열은 해당 요소가 값 형식이어도 값 타입이 아니고 <code>참조</code> 타입이다.<pre><code class="language-C#">  int[] nums = [1,2,3,4,5];</code></pre>
</li>
</ul>
<h3 id="6-리터럴-값-타입">6. 리터럴 값 타입</h3>
<ul>
<li>아래 설명 보세요.<blockquote>
<p>리터럴(Literal) 이란??    </p>
<ul>
<li>코드에서 직접적으로 값을 표현하는 고정된 값을 말한다.<br>즉, 프로그램 실행 중 변하지 않는 <code>상수</code>값을 의미한다.<br>근데 이게 진짜 상수를 의미한다기보다는 <code>어떤 데이터 타입</code>인지에 따라 다르게 처리된다.<br>즉, 각 리터럴은 기본적으로 자신의 데이터 형식을 갖고 있고 필요에 따라 명시적으로 지정할 수 있다.    </li>
<li>그니까 이게 뭔소리냐면... 변수 생성 할떄 값 넣을때 그거! 그게 리터럴 값이라는 소리다<pre><code class="language-C#">int num = 1;
long lNum = 1L;
uint uNum = 1U;
double dNum = 1.0;
float fNum = 1.0f;
string str = &quot;abc&quot;;
char chr = &#39;abc&#39;;
bool flag = true;
string s = &quot;The answer is &quot; + 5.ToString();
Type type = 12345.GetType();</code></pre>
</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="7-제네릭-타입">7. 제네릭 타입</h3>
<ul>
<li>실제 형식에 대한 자리 표시자로 사용되는 하나 이상의 형식 매개 변수를 사용해 형식을 선언할 수 있다.</li>
<li>인스턴스를 만들 때 구체적 형식을 제공하며 해당 형식을 제네릭 형식이라 한다.</li>
<li>형식 매개 변수를 사용하면 각 요소를 개체로 변환 할 필요 없이 같은 클래스를 재사용해 요소 형식을 포함할 수 있다.</li>
<li><small><strong><em>이건 좀 써보면서 익숙해지기 전까지는 이게 왜 필요한가 싶으니까 한 번 씩 사용에 대해서 고민 해보면 된다.</em></strong></small></li>
<li>이건 후술 필요시, 제네릭 <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/types/generics">공식문서</a> 참조<pre><code class="language-C#">public T outT&lt;T&gt;(T num)
{
    return num;
}</code></pre>
<blockquote>
<p>제네릭 타입
이건 swift에도 있는 그 제네릭 생각하면 된다.</p>
</blockquote>
</li>
</ul>
<h3 id="8-암시적-형식-무명-형식-및-nullable-값-형식">8. 암시적 형식, 무명 형식 및 nullable 값 형식</h3>
<ul>
<li><code>var</code> 키워드를 사용해 클래스 멤버가 아닌 로컬 변수를 암시적으로 형식화할 수 있다.</li>
</ul>
<blockquote>
<p>var 키워드</p>
<ul>
<li>var 키워드의 경우에는 프로그램이 변수나 상수 선언시 형식을 유추해서 저장을 할 수 있게 하는 키워드이다.</li>
<li><code>전역변수로 생성할 수 없으며</code> 지역변수로 class나 메서드 내부에서 생성을 해서 사용 할 수 밖에 없다.</li>
<li>당연하게도 형식 추론을 해야 하는 키워드 이므로 성능에는 좋지 않은 결과를 만들어 잦은 사용은 좋지 못하다</li>
<li>만능처럼 보일 수 는 있는데 너무 자주 쓰면 휴먼에러도 발생할 수 있고 좋지는 않으나 쓸 수 밖에 없거나 쓰는게 좋은 경우도 당연하게 있다.<pre><code class="language-C#">// 이렇게 할것을
List&lt;int&gt; list = new List&lt;int&gt;();
// 이렇게 할 수 있다.
var list2 = new List&lt;int&gt;();</code></pre>
</li>
<li>형식 추론이 나쁜 이유가 추론을 해야하니까 그 추론을 하기 위한 작업이 따로 들어가야 해서 그 동작에 대한 시간이 걸리니 응용프로그램이 느려지거나,</li>
<li>그에 따른 이슈가 발생하는건데 위에 처럼 애초에 <code>타입을 명시하지 않지만 추론으로 확정</code>하는 방법을 사용하면 꽤 보는게 편해질 수 있다.</li>
<li>var 의 사용을 남용하는건 좋지 않지만 적절히 <code>힌트</code>를 줘가면서 사용하는건 나쁘지 않아 보인다.</li>
</ul>
<p>당연하게도 이런건 swift에도 비슷한 화제가 나온적이 있다.</p>
</blockquote>
<h4 id="nullable-값-형식">nullable 값 형식</h4>
<ul>
<li>일반적인 값 형식은 null을 가질 수 <code>없다</code></li>
<li>그러나!! 형식 뒤에 <code>?</code>를 추가하면 null 허용 값 타입을 만들 수 있다.</li>
<li>값이 null 일 수 있는 DB의 데이터 전달 같은거 하거나 다른 상황에서 유용하다.</li>
<li><a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/builtin-types/nullable-value-types">공식문서</a>를 참조</li>
</ul>
<h3 id="9-컴파일-시간-형식-및-런타임-형식">9. 컴파일 시간 형식 및 런타임 형식</h3>
<ul>
<li><p>변수의 컴파일 시간과 런타임 형식은 서로 다를 수 있다.</p>
</li>
<li><p>이게 컴파일 시간 형식은 소스 코드에서 선언되거나 유추되는 변수의 형식인데, 런타임 형식은 해당 변수에서 참조하는 인스턴스의 형식이다.</p>
<pre><code class="language-C#">string msg = &quot;Hello My name is SEAN&quot;;
IEnumerable&lt;char&gt; someChar = &quot;Hello My name is SEAN&quot;;</code></pre>
</li>
<li><p>위의 코드에서 보면 두 변수의 런타임 형식은 전부 string 인데, 컴파일 시간은 처음은 object, 두번째 줄은 IEnumerable<char> 이다.</p>
</li>
<li><p>두 형식이 변수에 대해 다른 경우 컴파일 시간 형식과 런타임 형식이 적용되는 경우를 이해 하는 것이 더 중요하다.</p>
<ul>
<li>컴파일 시간 형식에 따라 컴파일러가 수행하는 모든 작업이 결정된다.     </li>
</ul>
<p><small><em>@&gt;--- 근데 이거 좀 코드 짜는데 딱히 막 이렇게까지 따져서 하기에는 <code>지금</code> 할건 아닌거 같으니 여기까지..</em></small></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[C#]  공식문서 학습하기 1 : ~프로그램 구조]]></title>
            <link>https://velog.io/@sean_kk/C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-1-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C</link>
            <guid>https://velog.io/@sean_kk/C-%ED%95%99%EC%8A%B5%ED%95%98%EA%B8%B0-1-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C</guid>
            <pubDate>Fri, 20 Sep 2024 00:35:53 GMT</pubDate>
            <description><![CDATA[<p>참고 자료: <a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/tour-of-csharp/">MS Document</a></p>
<blockquote>
<ul>
<li>C#이 생각보다 자료가 많이 없어서 그냥 공식문서 보고 정리하려고 한다.
일단은 <code>공식문서</code>상의 구조를 따라가려고 하는데 내용은 내 맘대로 써질 예정</li>
<li>본질이 iOS 개발자라 많이 비교하면서 학습을 진행할 예정이다.<ul>
<li>@&gt;--- 이런 기호와 함께 &#39;기울임&#39;, &#39;작게&#39; 표시 되면 개인적인 생각이다.</li>
</ul>
</li>
<li>당연 이건 다른 언어를 했던 사람들이 C# 공부시 그나마 조금 편하라고 만들어 보는거지 아예 코드 짜는 사람이 처음이라면 이해 안될 수 있는다.    </li>
<li>이해 안되는건 뒤에 나올 내용이 앞에 나와서 그러는거니까 첨보는 단어면 일단 넘어가면 뒤에 다시 나온다.</li>
</ul>
</blockquote>
<h2 id="프로그램-구조">프로그램 구조</h2>
<p><small><em >@&gt;--- 둘러보기가 있었는데 과감히 Skip</em></small></p>
<h3 id="1-c-프로그램의-일반적인-구조체">1. C# 프로그램의 일반적인 구조체</h3>
<ul>
<li>아주 아주 기본적인 구조<pre><code class="language-C#">using System;
</code></pre>
</li>
</ul>
<p>namespace LearnMSDoc
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(&quot;Hello World!&quot;);
        }
    }
}</p>
<pre><code>
#### 코드 분석
- 위 예제의 코드를 한 줄 한 줄 봐보자
```C#
using System;</code></pre><ol>
<li>해당 네임스페이스를 사용하는것은 <code>System</code> 네임스페이스를 포함시기키 위한 선언이다.<br>사용하게 되면 System 내부의 클래스나 메서드를 사용하게 되는데 여기선 일단 아래 코드와 연관이 있다.<pre><code class="language-C#">// 사용시
Console.WriteLine(&quot;Hello World!&quot;);
</code></pre>
</li>
</ol>
<p>// 미사용시
System.Console.WriteLine(&quot;Hello World!&quot;);</p>
<pre><code>&lt;small&gt;&lt;em&gt;@&gt;--- `import UIKit` 생각하면 될듯&lt;/em&gt;&lt;/small&gt;

2. 그럼 겸사겸사 아래 구문에 대한 해석도 완료된다.
- `LearnMSDoc` 네임스페스 안의 `Program` 클래스의 `Main`을 호출하면 콘솔이 찍힌다.    
&lt;small&gt;&lt;em&gt;@&gt;--- 진입접이니 뭐니 하는 문제는 그 다음 이야기이다.***&lt;/em&gt;&lt;/small&gt;
```C#
namespace LearnMSDoc
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(&quot;Hello World!&quot;);
        }
    }
}</code></pre><ol start="3">
<li><code>static void Main(string[] args)</code> 이 구문에 대해서 알아보자</li>
</ol>
<ul>
<li><p>C# 프로그램의 진입점으로 해당 함수를 사용하게 되는데 이제 프로그램이 <strong>가장 먼저</strong> 실행되는 C#에서의 시작이다.</p>
<ol>
<li>선언: static<ul>
<li>정적 메서드 표시 </li>
<li>새 인스턴스 생성 없이 호출이 가능 해야 하니 Static 선언을 함</li>
</ul>
</li>
<li>반환값: void<ul>
<li>반환은 없다.</li>
</ul>
</li>
<li>매개변수: string[] args<ul>
<li>명령줄 인수를 받는건데 외부에서 전달된거 받는건데 이건 나중에 쓸떄나 설명하는걸로</li>
</ul>
</li>
</ol>
</li>
<li><p>C# 내에는 하나의 진입점만 있을 수 있으므로 Main 메서드가 2이상 있는 경우 진입점으로 사용할 메서드를 따로 지정을 해야 한다. 
<Small><em>@&gt;--- 이렇게 까지는 딥하게 안할거 같으니 일단 넘어가자.</em></small></p>
</li>
<li><p>근데 Main을 안쓰고도 그냥 파일의 최상위 문을 진입점으로 사용할 수 있다. 
<small><em>@&gt;--- 그냥 Main쓰는걸로..</em></small></p>
</li>
<li><p>당연하게도 Main의 생성방법은 저게 다가 아니다.</p>
<pre><code class="language-C#">static void Main() { }
static int Main() { }
static void Main(string[] args) { }
static int Main(string[] args) { }
static async Task Main() { }
static async Task&lt;int&gt; Main() { }
static async Task Main(string[] args) { }
static async Task&lt;int&gt; Main(string[] args) { }</code></pre>
<p><a href="https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/program-structure/main-command-line">기본 메서드</a></p>
</li>
<li><p>이에 대한 조금 deep 한 설명과 사용방법은 해당 링크를 참조 할것</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Xcode] Quick Help 사용 방법 (feat. Markup)]]></title>
            <link>https://velog.io/@sean_kk/Xcode-Quick-help-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95-feat.-Markup</link>
            <guid>https://velog.io/@sean_kk/Xcode-Quick-help-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95-feat.-Markup</guid>
            <pubDate>Fri, 02 Jun 2023 00:31:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--markup-formattion-reference">참고자료 : <a href="https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/MarkupFunctionality.html#//apple_ref/doc/uid/TP40016497-CH54-SW1">Markup Formattion Reference</a></h4>
</blockquote>
<h1 id="시작">시작</h1>
<h2 id="시작글">시작글</h2>
<p>나는 개인적으로 코드를 짜거나 보면서 문서화를 하는걸 선호하는 편이고 그렇게 하고자 노력을 많이 하는데 따로 Pages 나 Numbers 같은걸 켜서 하기에는 번잡스럽고 귀찮기도 해서 Xcode의 <code>Quick Help</code> 기능을 많이 사용한다.</p>
<p>그래서 어떻게 하면 조금 더 <code>프로답게?</code> 작성을 할 수 있는 방법이 뭐가 있을까 알아보다가 알게된 것들을 이곳에 적어보려 한다.</p>
<h3 id="quick-help">Quick Help</h3>
<p>Quick Help(이하 퀵헬프)는 Xcode 내부에서 2가지 방법으로 확인을 할 수 있다.</p>
<ol>
<li>확인을 원하는 오브젝트위에 커서를 옮기고 <code>Inspector</code> 탭의 <code>Show Quick Help inspector</code> 에서 확인 가능</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/b5f115af-2724-4de2-81c8-d51679704086/image.png" alt=""></p>
<ol start="2">
<li><code>Option</code> 키를 누르면서 확인을 하고 싶은 오브젝트를 클릭을 해서 확인 가능</li>
</ol>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/c4c9d1c3-ba21-4972-a6d4-8ac9dc03035d/image.png" alt=""></p>
<ul>
<li>개인적으로 퀵헬프 확인을 위해서는 1번의 방법을 더 많이 사용을 하는 편이다.</li>
</ul>
<h1 id="분석">분석</h1>
<h2 id="markup-functionality">Markup Functionality</h2>
<p>퀵헬프는 마크업으로 작성을 할 수 있다. 마크업에 대한 정보는 맨 위의 참고자료를 확인하면 조금 더 자세하게 알 수 있을것이다.</p>
<p>참고자료에서 <code>Functionality</code> 라는 항목이 존재하는데 .swift 또는 .playground 에서 사용을 할 수 있는 항목 들에 대해서 구분해서 작성을 해놓았다.</p>
<p>해당 표에 적혀 있는 기능들 중에서 나는 playground 에서만 사용이 된다고 적힌것은 제외를 하고 실험을 해봤다.</p>
<ul>
<li>목록
<img src="https://velog.velcdn.com/images/sean_kk/post/3cb058f0-6250-4a76-b6d7-9fb51f85ef2d/image.png" alt=""> <img src="https://velog.velcdn.com/images/sean_kk/post/6c1164a2-1baf-485d-892d-84c6f83e2ec8/image.png" alt=""> <img src="https://velog.velcdn.com/images/sean_kk/post/478ee4f9-46c5-4d8d-99be-59827ca20346/image.png" alt=""></li>
</ul>
<h2 id="기초">기초</h2>
<p>퀵헬프는 코드내에 직접 기록하는 방식이고 원하는 오브젝트 <code>위</code>에 <code>///</code> 나 <code>/** (퀵헬프 내용) */</code> 같이 블럭으로 묶어서 만든다.</p>
<p>아래와 같은 방법으로 생성한다.</p>
<pre><code class="language-swift">/// number는 상수이다.
///
/// number는 항상 0이다.
let number: Int = 0</code></pre>
<pre><code class="language-swift">/** 
number는 상수이다.

number는 항상 0이다.
*/
let number: Int = 0</code></pre>
<p>또는 </p>
<pre><code class="language-swift">/** number는 상수이다.

number는 항상 0이다. */
let number: Int = 0</code></pre>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/97c6f639-65b6-4bd7-9340-0ef91b9b3686/image.png" alt=""></p>
<p>보면 알다시피 <code>맨 위</code>의 내용은 항상 <code>Summary</code> 부분에 입력이 되며 <code>한 줄 비우고</code> 아래에 써지는 내용은 <code>Discussion</code> 부분에 입력이 된다.</p>
<blockquote>
<p>대충 이해하기 쉽게 적어보자면
Summary == Quick Help의 제목
Discussion == Quick Help의 내용
이렇게 이해하면 쉽다.</p>
</blockquote>
<p>그리고 내용을 입력할때 입력을 하다 줄바꿈을 하고 싶은 경우에는 일반적으로 글을 쓸때는 한줄 바로 밑에 작성을 하지만 마크업에서는 <code>한 줄을 비우고</code> 작성을 해야 원하는 대로 한 줄 밑에 글이 작성이 된다.</p>
<pre><code class="language-swift">/// number는 상수이다.
///
/// number는 
///
/// 항상 0이다.
let number: Int = 0</code></pre>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/1e0230fb-a3f4-4f17-a8f0-c7c1196daf4f/image.png" alt=""></p>
<p>이 정도만 알아도 퀵헬프 작성에 큰 무리는 없지만 조금 더 표시하기 좋게 만들어 줄 기능에 대해서 알아보고자 한다.</p>
<h2 id="들어가기-전에">들어가기 전에</h2>
<blockquote>
<p>직접 테스트를 해보면서 알게 된건데 아무리 공식문서에 올라와있는 기능이라고 해도 직접 해보니 안되는 기능들이 존재 했다.
왜 그런가 History를 가서 확인을 하니 이러했다....
<img src="https://velog.velcdn.com/images/sean_kk/post/d77532f4-b0e0-4ba3-b3a3-0b4847cb9ccb/image.png" alt=""></p>
</blockquote>
<blockquote>
<p>또한 작성하는 오브젝트에 따라서 되는 기능이 있고 안되는 기능이 존재했다.</p>
</blockquote>
<blockquote>
<p>특정한 어느 기능은 원하는 규칙을 맞추지 않으면 동작하지 않는다.</p>
</blockquote>
<pre><code>기호 기능이름: (필수)내용
(선택)내용</code></pre><ul>
<li>기호에는 <code>- * +</code> 세 가지중 하나를 써준다.</li>
<li>기능 이름에는 위에 적어둔 항목을 써준다.</li>
<li>그 옆에 <code>(필수)</code> 적힌 곳에 내용을 써주지 않고 <code>(선택)</code> 에만 내용을 작성하게 된다면 해당 내용은 퀵헬프에 나타나지 않는다.
코드상에서 <code>바로 옆에 써야 사용가능</code> 이라고 표현</li>
</ul>
<h2 id="a--d">A ~ D</h2>
<blockquote>
<h5 id="항목-attention-author-authors-block-comment-bug-bulleted-lists-code-block-code-voice-complexity-copyright-date">항목: Attention, Author, Authors, Block Comment, Bug, Bulleted Lists, Code Block, Code Voice, Complexity, Copyright, Date</h5>
</blockquote>
<p>해당 항목들에 대해서는 따로 되거나 안되는 기능이 존재하지 않고 모든 오브젝트에서 동일한 동작을 보였다.</p>
<p><code>Attention, Author, Authors, Bug, Complexity, Copyright, Date</code>
항목의 경우에는 특별하게 정해진 작성 규칙이 존재한다.(<code>들어가기 전에 참고</code>)</p>
<pre><code class="language-swift">/// Summary 입력부분
///
/// 기능 테스트
/// - Attention: 바로 옆에 써야 사용 가능
///
/// - Author: 바로 옆에 써야 사용 가능
///
/// - Authors: 바로 옆에 써야 사용 가능
///
/// - Bug: 바로 옆에 써야 사용 가능
///
/// ```
/// code는 이렇게 씁니다.
/// ```
///
/// 이건 `CodeVoice`입니다.
///
/// 이건 Non CodeVoice 입니다.
///
/// - 기호 점 표시 가능
///
/// - Complexity: 바로 옆에 써야 사용 가능
///
/// - Copyright: 바로 옆에 써야 사용 가능
///
/// - Date: 바로 옆에 써야 사용 가능
class QuickHelpTest{}</code></pre>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/75d51359-da3d-4583-bd44-132646848568/image.png" alt=""></p>
<h2 id="e--l">E ~ L</h2>
<blockquote>
<h5 id="항목-emphasisitalics-escapes-experiment-headings-horizontal-rules-images-important-invariant-link-reference-links">항목: Emphasis(Italics), Escapes, Experiment, Headings, Horizontal Rules, Images, Important, Invariant, Link Reference, Links</h5>
</blockquote>
<p><code>Experiment, Important, Invariant</code>
항목의 경우에는 특별하게 정해진 작성 규칙이 존재한다.(<code>들어가기 전에 참고</code>)</p>
<p><code>Headings, Horizontal Rules, Images</code>
항목의 경우에는 문서상에 있는 방법으로 만들어도 동작하지 않는다.
<code>Headings</code> 의 같이 <code>#, ##, ###</code> 의 경우에는 뒤에 작성한 퀵헬프까지 나타나지 않게 하기에 동작하지 않는 항목의 경우에는 아예 시도도 하지 말것.</p>
<p><code>Link</code>
항목의 경우에는 문서에 적혀있는 방법 말고 해당 코드에서 사용한 방법으로 작성을 해야 동작이 된다.</p>
<pre><code class="language-swift">/// Summary 입력부분
///
/// 기능 테스트
/// *Italics* | 일반
///
/// \* Escaped 표현, 앞에 * 표시가 붙음
///
/// - Experiment: 바로 옆에 써야 사용 가능
///
/// Heading : ##
///
/// Horizontal Rules :  * * * * *
///
/// ![Xcode icon](http://devimages.apple.com.edgekey.net/assets/elements/icons/128x128/xcode.png &quot;Hover&quot;)
/// 이미지 그리는것도 안됨
///
/// - Important: 바로 옆에 써야 사용 가능
///
/// - Invariant: 바로 옆에 써야 사용 가능
///
/// [링크] : https://sean-k.kro.kr/ &quot;Hover Text&quot;
/// 근데 위 방법대로 하면 링크 안 만들어짐
///
/// [링크](https://sean-k.kro.kr)
/// 이렇게 작성해야 링크 생성됨
class QuickHelpTest{}</code></pre>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/e3d047be-4856-4079-b5a4-6ffe7ea95338/image.png" alt=""></p>
<h2 id="m--r">M ~ R</h2>
<blockquote>
<h5 id="항목-note-numbered-lists-parameter-parameters-postcondition-precondition-remark-returns-requires">항목: Note, Numbered Lists, Parameter, Parameters, Postcondition, Precondition, Remark, Returns, Requires</h5>
</blockquote>
<p><code>Note, Postcondition, Precondition, Remark, Requires</code>
항목의 경우에는 특별하게 정해진 작성 규칙이 존재한다.(<code>들어가기 전에 참고</code>)</p>
<p><code>Parameter, Parameters, Returns</code>
항목의 경우에는 <code>Class, Struct, Enum, let, var</code> 를 사용한 오브젝트에는 사용이 불가능하나 <code>func, typealias</code> 를 사용한 오브젝트에서는 사용이 가능하다.</p>
<p><code>Numbered Lists</code> 항목은 숫자로 리스트를 만들 수 있게 해주며 1,2,3,4,... 와 같이 숫자를 나열식으로 구현을 해도 되고 아니면 코드에 보이는것처럼 1,1,1,... 이런식으로 나열을 해도 같은 root의 depth에 위치한 숫자면 카운트를 자동으로 해준다.</p>
<p><code>Parameters</code> 항목의 경우에는 또 다른 특별한 작성 규칙이 존재한다.</p>
<pre><code>- Parameters:
    - parameter 이름: parameter 내용
    - parameter 이름: parameter 내용</code></pre><p><code>Parameters</code> 와 <code>Returns</code> 항목은 파라미터와 반환값이 있을 경우에만 사용이 가능한건가 싶어서 <code>Closure</code> 를 사용한 변수를 정의해서 퀵헬프를 작성을 해보았으나 마찬가지로 그냥 일반 변수와 동일하게 취급해 퀵헬프가 작성되지 않았다.
즉 <code>func</code> 과 <code>typalias</code> 가 아니면 아예 동작하지 않는 것을 확인했다.</p>
<pre><code class="language-swift">/// Summary 입력부분
///
/// 기능 테스트
///
/// - Note: 바로 옆에 써야 사용 가능
///
/// 1. Number List
///     1. a
/// 1. Number List
///     1. b
///
/// - Parameters: 특정 오브젝트 사용 불가
///     - A: a
///
/// - Parameter a: 특정 오브젝트 사용 불가
///
/// - Postcondition: 바로 옆에 써야 사용 가능
///
/// - Precondition: 바로 옆에 써야 사용 가능
///
/// - Remark: 바로 옆에 써야 사용 가능
///
/// - Returns: 특정 오브젝트 사용 불가
///
/// - Requires: 바로 옆에 써야 사용 가능
class QuickHelpTest{}</code></pre>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/d33fe43d-82e0-4459-8c69-0631e6046571/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/9ef78088-a95b-449a-a9ed-1bbe405c9fdd/image.png" alt=""></p>
<h2 id="s--z">S ~ Z</h2>
<blockquote>
<h5 id="항목-see-also-since-single-line-comment-strongbold-throws-to-do-version-warning">항목: See Also, Since, Single Line Comment, Strong(Bold), Throws, To Do, Version, Warning</h5>
</blockquote>
<p><code>Since, Throws, To Do, Version, Warning</code>
항목의 경우에는 특별하게 정해진 작성 규칙이 존재한다.(<code>들어가기 전에 참고</code>)</p>
<p><code>SeeAlso</code> 항목의 경우에는 문서내의 작성법으로 시도를 해봤지만 제대로 동작하지 않는것을 확인했다.</p>
<pre><code class="language-swift">/// Summary 입력부분
///
/// 기능 테스트
///
/// SeeAlso: [Link](https://sean-k.kro.kr) 는 안되는데여?
///
/// - Since: 바로 옆에 써야 사용 가능
///
/// **BOLD** | 일반
///
/// - Throws: 바로 옆에 써야 사용 가능
///
/// - ToDo: 바로 옆에 써야 사용 가능
///
/// - Version: 1.0.0
///
/// - Warning: 바로 옆에 써야 사용 가능
class QuickHelpTest{}</code></pre>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/b5de7f80-0c24-4d10-b6f1-f9bdbb1e0a3a/image.png" alt=""></p>
<h2 id="마치며">마치며</h2>
<p><code>Quick Help</code> 기능은 어떻게 쓰냐에 따라서 코드가 지저분해질 수 도 있고 애초에 필요없는 항목이 될 수도 있다.
하지만 개인적으로 퀵헬프는 유용한 기능이라고 생각해 이렇게 알아보게 되었고 정리를 해봤다.</p>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WebKit] WKUserContentController : Web 과 상호작용]]></title>
            <link>https://velog.io/@sean_kk/WebKit-WKUserContentController-Web-%EA%B3%BC-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9</link>
            <guid>https://velog.io/@sean_kk/WebKit-WKUserContentController-Web-%EA%B3%BC-%EC%83%81%ED%98%B8%EC%9E%91%EC%9A%A9</guid>
            <pubDate>Fri, 26 May 2023 05:58:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--wkusercontentcontroller-공식문서">참고자료 : <a href="https://developer.apple.com/documentation/webkit/wkusercontentcontroller">WKUserContentController 공식문서</a></h4>
</blockquote>
<h1 id="시작">시작</h1>
<p>[Document: WKUserContentController] (<a href="https://developer.apple.com/documentation/webkit/wkusercontentcontroller">https://developer.apple.com/documentation/webkit/wkusercontentcontroller</a>)</p>
<h3 id="개요">개요</h3>
<ul>
<li>JS 코드와 Web View 간 상호작용을 관리하고 Web View에서 컨텐츠를 필터링 하는데 사용되는 오브젝트</li>
</ul>
<h3 id="버전">버전</h3>
<ul>
<li>iOS 8.0+</li>
<li>macOS 10.10+</li>
</ul>
<h3 id="번역">번역</h3>
<h4 id="overview">Overview</h4>
<p>WKUserContentController 오브젝트는 앱과 웹 뷰에서 실행 중인 JavaScript 코드 사이의 연결을 제공한다.
다음의 오브젝트를 사용해 다음의 작업을 수행할 수 있다.</p>
<ul>
<li>웹 뷰에서 실행 중인 웹 페이지에 JavaScript 코드를 주입</li>
<li>앱의 네이티브 코드로 연결되는 사용자 정의 JavaScript 함수를 설치</li>
<li>제한된 콘텐츠가 로드되지 않도록 사용자 정의 필터를 지정</li>
</ul>
<p>전체적으로 웹 뷰 설정으로 WKUserContentController 오브젝트를 생성하고 구성한다.
웹 뷰 생성 전 해당 오브젝트를 WKWebViewConfiguration 오브젝트의 userContentController 프로퍼티에 할당한다.</p>
<h1 id="분석">분석</h1>
<h2 id="topic">Topic</h2>
<h3 id="커스텀-스크립트-추가-및-제거">커스텀 스크립트 추가 및 제거</h3>
<ol>
<li>func addUserScript(WKUserScript)<ul>
<li>지정된 스크립트를 웹 페이지의 콘텐츠에 추가</li>
</ul>
</li>
<li>func removeAllUserScripts()<ul>
<li>웹 뷰에서 모든 사용자 스크립트를 제거</li>
</ul>
</li>
<li>var userScripts: [WKUserScript]<ul>
<li>user content controller 와 연결된 사용자 스크립트</li>
</ul>
</li>
</ol>
<hr>
<h3 id="message-handler-추가-및-제거">Message Handler 추가 및 제거</h3>
<ol>
<li>func add(WKScriptMessageHandler, name: String)<ul>
<li>JS 코드에서 호출 할 수 있는 핸들러 설치</li>
</ul>
</li>
<li>func add(WKScriptMessageHandler, contentWorld: WKContentWorld, name: String)<ul>
<li>JS 의 컨텐츠 환경에서 호출 할 수 있는 핸들러 설치</li>
</ul>
</li>
<li>func addScriptMessageHandler(WKScriptMessageHandlerWithReply, contentWorld: WKContentWorld, name: String)<ul>
<li>JS 의 회신을 반환하는 핸들러 설치</li>
</ul>
</li>
<li>func removeScriptMessageHandler(forName: String)<ul>
<li>JS 코드에서 지정된 이름을 가진 핸들러 제거</li>
</ul>
</li>
<li>func removeScriptMessageHandler(forName: String, contentWorld: WKContentWorld)<ul>
<li>JS 코드의 컨텐츠 환경에서의 핸들러 제거</li>
</ul>
</li>
<li>func removeAllScriptMessageHandlers(from: WKContentWorld)<ul>
<li>JS 코드의 컨텐츠 환경에서 모든 핸들러 제거</li>
</ul>
</li>
<li>func removeAllScriptMessageHandlers()<ul>
<li>user content controller 와 연결된 모든 핸들러 제거</li>
</ul>
</li>
<li>protocol WKScriptMessageHandler<ul>
<li>웹 페이지에서 실행되는 JS 코드의 매시지 수신 위한 인터페이스</li>
</ul>
</li>
<li>protocol WKScriptMessageHandlerWithReply<ul>
<li>웹 페이지에서 실행되는 JS 코드의 메시지 응답 위한 인터페이스</li>
</ul>
</li>
</ol>
<hr>
<h3 id="컨텐츠-규칙-추가-및-제거">컨텐츠 규칙 추가 및 제거</h3>
<ol>
<li>func add(WKContentRuleList)<ul>
<li>컨텐츠 컨트롤러 오브젝트에 지정된 규칙 목록을 추가</li>
</ul>
</li>
<li>func remove(WKContentRuleList)<ul>
<li>컨텐츠 컨트롤러 오브젝트에서 지정된 규칙 목록 제거</li>
</ul>
</li>
<li>func removeAllContentRuleLists()<ul>
<li>컨텐츠 컨트롤러에서 모든 규칙 제거</li>
</ul>
</li>
<li>class WKContentRuleList<ul>
<li>웹 컨텐츠에 적용할 컴파일된 규칙의 리스트</li>
</ul>
</li>
</ol>
<hr>
<h2 id="사용">사용</h2>
<p>해당 기능을 사용하기 위해서는 웹 뷰는 코드로 생성을 해야 한다.
웹 뷰 초기화 함수의 <code>configuration</code>을 사용해야 하는데 이는 코드로만 접근이 가능하기 때문이다.</p>
<pre><code class="language-swift">let contentController = WKUserContentController()
let cofiguration = WKWebViewConfiguration()

contentController.add(self, name: &quot;MyInterfaceName&quot;)

configuration.userContentController = contentController

let webView = WKWebView(frame: .zero, configuration: configuration)</code></pre>
<p><code>WKScriptMessageHandler</code> 를 채택해 해당 프로토콜의 <code>func userContentController(WKUserContentController, didReceive: WKScriptMessage)</code> 함수로 이벤트를 받아온다.</p>
<p>WKScriptMessage 은 다음과 같은 형태를 띄고 있다.</p>
<pre><code class="language-swift">open class WKScriptMessage : NSObject {
    open var body: Any { get }
    weak open var webView: WKWebView? { get }
    @NSCopying open var frameInfo: WKFrameInfo { get }
    open var name: String { get }
    @available(iOS 14.0, *)
    open var world: WKContentWorld { get }
}</code></pre>
<p>프로토콜을 채택해서 함수로 이벤트를 받아오는 방법은 다음과 같다.</p>
<pre><code class="language-swift">class WebViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        let name = message.name
        if let body = message.body as? String {
            ...
        }
    }
}</code></pre>
<p>이런식으로 <code>message</code> 를 사용하여 받아온 값을 다루면 된다.</p>
<blockquote>
<p>이거 테스트 하는 방법은 웹을 직접 할 수 있는 분이 개발자용 도구 열어서 하라고는 하는데
필자는 진행하는 프로젝트에서 사용하는 부분인지라 다 구현이 되어있어서 따로 직접 웹 관련 무언가는 하지 않아서 해당 부분에 대해서는 따로 말을 해줄 수는 없다.</p>
</blockquote>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Deprecated] UIKit : keyWindow]]></title>
            <link>https://velog.io/@sean_kk/Deprecated-UIKit-keyWindow</link>
            <guid>https://velog.io/@sean_kk/Deprecated-UIKit-keyWindow</guid>
            <pubDate>Fri, 26 May 2023 02:23:51 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<p>코드를 작성하다 만나게 되는 Deprecated된 요소들을 앞으로 어떻게 사용해야 할 지 알아보는 게시글이 될 것이다.</p>
</blockquote>
<blockquote>
<h2 id="deprecated">Deprecated</h2>
</blockquote>
<ul>
<li>중요도가 떨어져 더 이상 사용하지 않거나 아직은 사용하지만 새로운 기능의 존재로 사라지게 될 기능</li>
</ul>
<h1 id="시작">시작</h1>
<blockquote>
<h4 id="property-keywindow"><a href="https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow">Property: keyWindow</a></h4>
</blockquote>
<h3 id="버전">버전</h3>
<ul>
<li>iOS 2.0 ~ 13.0</li>
<li>iPadOS 2.0 ~ 13.0</li>
</ul>
<h3 id="용도">용도</h3>
<ul>
<li>이 프로퍼티는 가장 최근에 보낸 makeKeyAndVisible()메시지를 윈도우즈 배열의 UIWindow 오브젝트를 보유한다. </li>
</ul>
<h3 id="선언">선언</h3>
<pre><code class="language-swift">var keyWindow: UIWindow? { get }</code></pre>
<h1 id="분석">분석</h1>
<ul>
<li>해당 프로퍼티의 경우 13.0 부터 Deprecated 되어 있기에 3의 방법을 사용해서 처리를 해주면 된다.</li>
<li>실제 코드에서는 해당 프로퍼티를 어떤 방식으로 사용을 할 지 몰라서 일단 공부하며 알게 된 모든 방법에 대해서 다 작성했다.</li>
<li>대체한 프로퍼티가 또 Deprecated 되기에 해당 부분에 대해서는 따로 작성이 필요한 것으로 보인다.</li>
</ul>
<h3 id="1-기존-사용-방법">1. 기존 사용 방법</h3>
<pre><code class="language-swift">UIApplication.shared.keyWindow?.rootViewController = vc</code></pre>
<hr>
<h3 id="2-변경될-사용-방법">2. 변경될 사용 방법</h3>
<p>iOS 13.0 이상에서는 이렇게 사용했다.</p>
<pre><code class="language-swift">UIApplication.shared.windows.first?.rootViewController = vc</code></pre>
<p>하지만 <code>windows</code> 가 15.0 부터 Deprecated 되기에 해당 부분을 다음과 같은 방법으로 변경해야 한다.
(해당 방법에 대해서는 view에서 접근하는거 말고 UIApplication 으로 접근하는 방법을 알게 되면 해당 부분에 대해서 수정 작업이 진행될 것이다.)</p>
<pre><code class="language-swift">view.window?.rootViewController = vc</code></pre>
<hr>
<h3 id="3-버전-고려한-방법">3. 버전 고려한 방법</h3>
<pre><code class="language-swift">if #available(iOS 15.0, *) {
    view.window?.rootViewController = vc
} else if #available(iOS 13.0, *){
    UIApplication.shared.windows.first?.rootViewController = vc
} else {
    UIApplication.shared.keyWindow?.rootViewController = vc
}</code></pre>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WebKit] WKWebsiteDataStore : 웹 뷰 데이터 관리]]></title>
            <link>https://velog.io/@sean_kk/WebKit-WKWebsiteDataStore-%EC%9B%B9-%EB%B7%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@sean_kk/WebKit-WKWebsiteDataStore-%EC%9B%B9-%EB%B7%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Fri, 26 May 2023 01:22:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--wkwebsitedatastore-공식문서">참고자료 : <a href="https://developer.apple.com/documentation/webkit/wkwebsitedatastore">WKWebsiteDataStore 공식문서</a></h4>
</blockquote>
<h1 id="시작">시작</h1>
<p><a href="https://developer.apple.com/documentation/webkit/wkwebsitedatastore">Document: WKWebsiteDataStore</a></p>
<h3 id="개요">개요</h3>
<ul>
<li>웹뷰의 쿠키, 디스크, 메모리 캐시 그리고 다른 데이터 타입을 관리</li>
</ul>
<h3 id="버전">버전</h3>
<ul>
<li>iOS 9.0+</li>
<li>macOS 10.11+</li>
</ul>
<h3 id="번역">번역</h3>
<h4 id="overview">Overview</h4>
<p>WKWebsiteDataStore 오브젝트를 사용하여 웹 사이트의 데이터를 관리 할 수 있다.
해당 오브젝트를 사용하여 다음과 같은 동작을 수행한다.</p>
<ol>
<li>웹 사이트가 사용하는 쿠키를 관리할 수 있다.</li>
<li>웹 사이트에서 저장하는 데이터의 타입에 대해서 알 수 있다.</li>
<li>웹 사이트에서 원하지 않는 데이터를 삭제 할 수 있다.</li>
</ol>
<p>웹뷰 생성 전에 데이터 스토어 오브젝트를 만들어 WKebViewConfiguration 오브젝트의 websiteDataStore 속성에 할당한다.
default() 메서드는 웹사이트 데이터를 디크스에 저장하는 데이터 스토어를 반환한다.
프리베잇한 브라우징을 구현하기 위해 nonPersistent() 메서드를 대신 사용하여 비영구적인 데이터 스토어를 만든다.</p>
<h1 id="분석">분석</h1>
<blockquote>
<p>그리 엄청 많은 구성이 존재하는게 아니고 Overview의 설명에 기반을 두기에 금방 이해가 가능하다.</p>
</blockquote>
<h2 id="topics">Topics</h2>
<h3 id="data-store-오브젝트-생성">Data Store 오브젝트 생성</h3>
<ol>
<li>default ()<ul>
<li>디스크에 저장된 곳에 접근</li>
</ul>
</li>
<li>nonPersistent()<ul>
<li>디스크가 아닌 메모리에 접근</li>
</ul>
</li>
</ol>
<h3 id="data-store-프로퍼티-검사">Data Store 프로퍼티 검사</h3>
<ol>
<li>isPersistent: Bool<ul>
<li>데이터를 디스크에 저장 할것인 지 결정</li>
</ul>
</li>
</ol>
<h3 id="cookie-store-검색">Cookie Store 검색</h3>
<ol>
<li>httpCookieStore: WKHTTPCookieStore<ul>
<li>웹 사이트의 HTTP 쿠키 관리하는 오브젝트</li>
</ul>
</li>
</ol>
<h3 id="특정한-타입의-데이터-검색">특정한 타입의 데이터 검색</h3>
<ol>
<li>func fetchDataRecords(ofTypes: Set&lt; String &gt;, completionHandler: ([WKWebsiteDataRecord]) -&gt; Void)<ul>
<li>data store 에 지정된 타입의 레코드를 가져온다.</li>
</ul>
</li>
<li>allWebsiteDataType() -&gt; Set&lt; String &gt;<ul>
<li>사용 가능한 모든 데이터 타입의 Set 을 반환</li>
</ul>
</li>
</ol>
<h3 id="특정한-타입의-데이터-제거">특정한 타입의 데이터 제거</h3>
<ol>
<li>func removeData(ofTypes: Set&lt; String &gt;, for: [WKWebsiteDataRecord], completionHandler: () -&gt; Void)<ul>
<li>하나 이상의 데이터 레코드에서 지정된 웹 사이트의 데이터를 제거</li>
</ul>
</li>
<li>func removeData(ofTypes: Set&lt; String &gt;, modifiedSince: Date, completionHandler: () -&gt; Void)<ul>
<li>지정 날짜 이후 변경된 웹 사이트 데이터 제거</li>
</ul>
</li>
</ol>
<hr>
<h2 id="사용">사용</h2>
<p>디스크 저장 데이터 초기화 위해 접근</p>
<pre><code class="language-swift">WKWebsiteDataStore.default()</code></pre>
<p>데이터를 가져 올때는 fetchDataRecords() 메서드 이용</p>
<pre><code class="language-swift">WkWebsiteDataStore.default()
    .fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), 
                      completionHandler: { records in
                          ...
                      })</code></pre>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Swift 기초 공부 - 2 : 기본]]></title>
            <link>https://velog.io/@sean_kk/Swift-Swift-%EA%B8%B0%EC%B4%88-%EA%B3%B5%EB%B6%80-2-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@sean_kk/Swift-Swift-%EA%B8%B0%EC%B4%88-%EA%B3%B5%EB%B6%80-2-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Tue, 23 May 2023 08:52:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--swiftorg">참고자료 : <a href="https://www.swift.org/">swift.org</a></h4>
</blockquote>
<h1 id="시작">시작</h1>
<h3 id="시작글">시작글</h3>
<ul>
<li><p>현재 나는 Objective-C 와 Swift 언어로 iOS앱의 개발을 할 수 있지만 뭔가가 부족한 느낌을 항상 받았다.
거진 다 아는 내용이지만 머리로는 알겠는데 말로 하거나 조금만 심화로 Deep 하게 들어가면 부족한 것을 너무 절실하게 느꼈다.
그래서 기초가 조금 더 탄탄했으면 했는데 그 기초를 다시 공부하면서 조금 더 탄탄하게 쌓을 생각이다.</p>
</li>
<li><p>당연하게도 이 글은 기초 공부지만 어느정도 swift를 아는 놈이 작성하는거라 문서를 보고 하지만 약간 야매같은 느낌으로 이 글을 딱딱하게 책을 읽듯 요약하는게 아니라 나만의 방식으로 유연한 요약을 목표로 할 생각이다.</p>
</li>
</ul>
<h1 id="분석">분석</h1>
<h2 id="상수와-변수">상수와 변수</h2>
<h3 id="상수와-변수-선언">상수와 변수 선언</h3>
<p><code>상수 : let , 변수 : var</code></p>
<pre><code class="language-swift">let x = 10
var y = 20</code></pre>
<p>여러개의 상수 또는 여러개의 변수 선언시 콤마로 구분해 한줄로 선언 가능</p>
<pre><code class="language-swift">let x = 10, y = 20</code></pre>
<p>자신이 선언한 해당 변수가 후에 코드내에서 저장 값이 변경이 되지 않는다면 <code>let</code> 으로 선언을 해야 한다. (안해도 상관은 없지만 경고가 뜬다.)</p>
<blockquote>
<p>앞으로 특별한 언급없이 변수라고 하는 경우에는 상수 또한 마찬가지로 적용되는 사항들이라고 알면된다.
둘은 다른것이지만 포스트 작성의 편의를 위해서 특별하게 구분하는 경우가 아니면 변수로 통일해서 작성을 하도록 하겠다.</p>
</blockquote>
<h3 id="타입-명시">타입 명시</h3>
<p>변수 선언시 저장할 수 있는 값의 종류를 명확하게 하기 위해 타입을 작성해준다.
swift는 <code>타입 세이프티</code>와 <code>타입 추론</code>이 있기에 사용될 타입을 거의 항상 유추할 수 있다.</p>
<blockquote>
<p>근데 아무리 추론이 있다고는 하지만 타입을 직접 명시해주는게 속도적인 측면에서도 그렇고 후에 유지보수 및 코드를 읽을때 변수 선언부만 보고도 이게 무슨 타입의 변수인지를 바로 알 수 있기에 타입 명시를 해주는게 좋다.</p>
</blockquote>
<pre><code class="language-swift">let num: Int = 10
let number = 10</code></pre>
<h3 id="이름">이름</h3>
<p>변수의 이름은 유니코드 문자를 포함해 대부분의 문자를 포함 할 수 있다.</p>
<pre><code class="language-swift">let num = 10
let 숫자 = 10
let 🍔 = 10</code></pre>
<p>하지만 공백, 수학적 기호, 화살표, 내부 사용 유니코드 스칼라 값, 선과 박스를 그리는 문자는 불가능
숫자는 사용이 가능하지만 이름의 시작으로는 사용할 수 없다.</p>
<p>변수를 선언하면 동일 이름으로 다시 선언할 수 없다.
다른 타입으로 저장하게 변경해 선언할 수 없다.
상수를 변수로 바꾸거나 그 역도 할 수 없다.</p>
<p>swift의 키워드와 동일한 이름으로 변수를 제공하고 싶다면 <code>백틱 (```)</code> 으로 묶으면 사용할 수 있는데 &quot;진짜로 무조건 이거를 써야만 해!&quot; 라는게 아니면 쓰지 말것.</p>
<h3 id="출력">출력</h3>
<p><code>print()</code> 함수를 사용하여 변수의 값을 콘솔 창에 출력할 수 있다.</p>
<hr>
<h2 id="주석">주석</h2>
<blockquote>
<p><code>...</code> 은 코드를 의미한다고 필자는 작성했다.</p>
</blockquote>
<p><code>// ...</code> : 한줄 주석
<code>/* ... */</code> : 여러줄 주석</p>
<hr>
<h2 id="세미콜론">세미콜론</h2>
<p><code>세미콜론 (;)</code> 은 필수 조건이 아니나 여러 구문을 한 줄 작성시에는 필수로 사용한다.</p>
<pre><code class="language-swift">let cat = &quot;Cat&quot;; print(&quot;\(cat)&quot;)</code></pre>
<hr>
<h2 id="정수">정수</h2>
<h3 id="범위">범위</h3>
<p><code>min</code> 과 <code>max</code> 프로퍼티를 통해 각각 최소값, 최대값을 가져올 수 있다.</p>
<h3 id="int">Int</h3>
<p>대부분의 경우 정수의 사이즈를 결정할 필요는 없다.
특정 크기의 정수로 작업하는게 아니라면 Int 를 사용하면 된다.
Int는 <code>-2,147,483,648</code> 과 <code>2,147,483,647</code> 사이의 값을 저장할 수 있으며 일반적 사용에는 문제가 없다.</p>
<h3 id="uint">UInt</h3>
<p>부호없는 정수 타입이 필요한 경우에만 사용하며 음수가 아니어도 Int를 더 선호한다.</p>
<hr>
<h2 id="부동-소수점-숫자">부동 소수점 숫자</h2>
<p>2가지의 부호를 가진 부동 소수점 숫자 타입을 제공한다.</p>
<ul>
<li>Double : 64-bit 숫자를 표기</li>
<li>Float : 32-bit 숫자를 표기</li>
</ul>
<blockquote>
<p>근데 부동소수점은 정확도의 차이가 존재한다.
가장 큰 예 중 하나가 <code>0.1 + 0.1 != 0.2</code>가 되는데 이는 부동 소수점이 가지는 소수점 때문에 발생하는 이슈이다.</p>
</blockquote>
<blockquote>
<p>Double는 최소 15자리의 소수점 정확도를 가지고 있으나 Float 는 6자리의 정확도를 가진다.
어느 타입으로 작업을 하는지는 코드에 따라 다르겠지만 일반적으로 <code>Double</code> 가 더 선호된다.</p>
</blockquote>
<hr>
<h2 id="타입-세이프티-와-타입-추론">타입 세이프티 와 타입 추론</h2>
<p>Swift는 type-safe 언어이다. 타입 세이프 언어를 사용하면 사용할 수 있는 값의 타입을 명확하게 알 수 있으며 String 이 필요한 경우 실수로 Int 를 전달 할 수 없다.</p>
<p>이로 인해 모든 변수의 타입을 지정해야 하는것은 아니다. swift는 특정 타입을 지정하지 않으면 적절한 타입으로 타입 추론을 사용하며 타입 추론을 통해 컴파일러는 코드를 컴파일 시 제공한 값을 검사해 특정 식의 타입을 자동으로 추론할 수 있다.</p>
<blockquote>
<p>타입 추론 좋은데 위에도 적었다시피 타입은 최대한 명시하는 편이 필자는 좋다는 생각을 갖고 있다.</p>
</blockquote>
<hr>
<h2 id="숫자-리터럴">숫자 리터럴</h2>
<p>정수 리터럴은 다음과 같이 쓸 수 있다.</p>
<ul>
<li>접두사 없다: 10진수</li>
<li><code>0b</code> 접두사 : 2진수</li>
<li><code>0o</code> 접수사 : 8진수</li>
<li><code>0x</code> 접두사 : 16진수</li>
</ul>
<p>숫자 리터럴은 읽기 쉽게 만드는 추가 포맷을 포함 할 수 있다. 정수와 부동소수점 모두 추가 0로 채워질 수 있으며 가독성을 위해 _ 포함이 가능하다.</p>
<pre><code class="language-swift">let paddingDouble: Double = 000123.456
let lineInt: Int = 1_000_000_000</code></pre>
<blockquote>
<p>만약에 이런게 필요하면 외워둘 필요 없이 그냥 구글 켜서 바로 검색하자 그게 훨씬 가성비가 좋다.</p>
</blockquote>
<hr>
<h2 id="숫자-타입-변환">숫자 타입 변환</h2>
<blockquote>
<p>이거 막 뭐라고 써있고 하던데 그냥 솔직히 다 볼 필요 없다.
요약하면 Int형은 Int 끼리 계산하도록 해야 하고 필요하다면 그 타입을 변환을 해줘야 한다! 라는 뜻이다.
|
이건 숫자뿐 아니라 타입간 타입 변환시에 많이 사용하는 방법인데 <code>SomeType(InitialValue)</code> 라는 방법으로 타입을 쓰고 괄호 안에 값을 넣어주면 된다. </p>
</blockquote>
<hr>
<h2 id="타입-별칭">타입 별칭</h2>
<p>타입 별칭은 이미 존재하는 타입을 다른 이름으로 정의할 때 사용한다.</p>
<pre><code class="language-swift">typealias Success = Bool</code></pre>
<blockquote>
<p>단순히 특정한 타입을 다른 이름으로 정의하는거라 가독성을 위해서 사용하는 경우에 매우 유용하다.</p>
</blockquote>
<hr>
<h2 id="부울-불리언-booleans">부울, 불리언 (Booleans)</h2>
<p><code>Bool</code> 타입은 <code>true</code> or <code>false</code> 두가지의 값만 가지므로 논리적참조시 사용된다.</p>
<blockquote>
<p>if의 조건으로 사용하기에 아주 좋은 타입으로 많이 사용하게 될것이다.
또한 <code>0 == false</code> 와 <code>1 == true</code> 가 되기에 해당 방법으로 사용할 수 도 있다. </p>
</blockquote>
<hr>
<h2 id="튜플-tuples">튜플 (Tuples)</h2>
<p>여러값을 단일 복합 값으로 그룹화 한다. 튜플 내부에는 어떠한 타입도 가능하기에 구성이 되는 요소들이 전부 같은 타입일 필요는 없다.</p>
<p>모든 타입의 튜플을 만들 수 있으며 원하는 만큼 다른 타입을 포함시킬 수 있다.
사용 방법은 아래와 같이 사용할 수 있다.</p>
<p>튜플 정의하고 인덱스를 사용해 개별 요소 값에 접근 할 수 있다.
튜플 분해시 _ 를 사용하여 튜플의 일부를 무시할 수 있다.
튜플 정의시 요소에 이름을 정할 수 있다. 요소에 이름이 있으면 요소로 접근 가능하다.</p>
<pre><code class="language-swift">let tupleTest = (123, &quot;ABC&quot;)
print(tupleTest.0)
// 123

let (numTuple, _) = tupleTest
print(numTuple)
// 123

let tupleTest2 = (numTuple2: 123, strTuple2: &quot;ABC&quot;)
print(tupleTest2.numTuple2)
// 123</code></pre>
<p>튜플은 간단한 값의 그룹에 유용하며 조금 복잡해지는 경우에는 구조체나 클래스를 사용하는것이 좋다.</p>
<hr>
<h2 id="옵셔널-optionals">옵셔널 (Optionals)</h2>
<p>옵셔널은 값이 없을 수 있는 경우에 사용한다.
옵셔널을 선언하는 경우에는 변수의 타입 뒤에 <code>?</code>를 입력하여 선언한다.</p>
<pre><code class="language-swift">let optionalTest: String? </code></pre>
<p>옵셔널에서 값이 없는 경우에 <code>nil</code>이라는 특수한 값을 지정하여 해당 상태를 나타낼 수 있다.
옵셔널이 아닌 변수에는 nil을 사용할 수 없고 만약 변수가 값이 없는 상태의 동작이 필요하다면 해당 변수는 옵셔널로 선언해야 한다.</p>
<h3 id="if-구문과-강제-언래핑">if 구문과 강제 언래핑</h3>
<p>if는 옵셔널과 nil을 비교해 옵셔널 타입에 값이 포함되어 있는지 확인할 수 있다.
<code>강제언래핑</code>은 <code>!</code>를 옵셔널 변수 뒤에 적어서 사용을 한다.
하지만 사용전에 값이 nil 이라면 런타입 에러가 나타나게 된다.</p>
<blockquote>
<p>! 로 강제 언래핑 하는건 하지 않도록 하자가 아니고 그냥 하지마!
어떻게든 아래서 나올 바인딩 해서 다 풀어서 사용 할 것!</p>
</blockquote>
<h3 id="옵셔널-바인딩">옵셔널 바인딩</h3>
<p>이때 사용하는 방법이 <code>ìf</code> 를 사용하는 방법과 <code>guard</code> 를 사용하는 방법이 있다.
두 방식의 차이점이라고 하면 if 구문에서 바인딩으로 생성된 경우에는 오직 if 구문 내에서만 사요잉 가능하나 guard 로 생성된 경우에는 해당 구문 다음 부터 사용이 가능하다.</p>
<pre><code class="language-swift">let str: Sting? = &quot;ABC&quot;
print(str)
//Optional(ABC)

if let str = str {
    print(str)
}
// ABC

guard let str = str as? String else { return }
print(str)
// ABC</code></pre>
<blockquote>
<p>guard 문의 경우에는 후에 자세하게 설명할 예정이니 지금은 저러한 방법이 있다는 거만 알 고 있음면 된다.</p>
</blockquote>
<h3 id="암시적으로-언래핑-된-옵셔널">암시적으로 언래핑 된 옵셔널</h3>
<p>때로는 처음에 설정 후 항상 값을 갖는것이 분명한 경우가 있다. 이러한 경우에 접근할 때마다 계속 확인하고 언래핑하는 작업을 할 필요는 없다.
그래서 <code>압시적으로 언래핑 된 옵셔널</code>을 정의하기 위해 <code>?</code> 대신에 <code>!</code>를 작성하여 암시적 언래핑 옵셔널을 선언해준다.</p>
<pre><code class="language-swift">let optionalTest: String? = &quot;Test Text&quot;
let optionalTest2: String! = &quot;Test Text&quot;</code></pre>
<p>해당 방법은 <code>!</code> 와 같다고 볼 수 도 있지만 약간 다른 방법으로 돌아간다고 볼 수 있다.</p>
<pre><code class="language-swift">let notOpt: String = optionalTest2
let strOpt = optionalTest2</code></pre>
<p>이렇게 2개의 상수가 선언이 되었을 때 <code>notOpt</code>의 경우에는 String 이라고 타입 명시가 되어있어 옵셔널이 아님을 나타내고 있다 그렇기에 해당 변수에는 옵셔널이 들어가면 안된다.</p>
<p>그러면 암시적으로 언래핑된 옵셔널 타입이던 optionalTest2는 강제적으로 언래핑 하여 해당 값을 할당한다.</p>
<p>하지만 밑에 <code>strOpt</code>의 경우에는 타입 추론이기에 기본적으로 옵셔널 값을 받게 된다.</p>
<blockquote>
<p>중요한건 나중에 nil 이 될 가능성이 0.0000000000001 이라도 있다면 <code>!</code> 는 사용하는게 아니다.
암시적 언래핑이고 뭐고 필자는 코드에 !가 들어가는것을 좋아하지 않는다.</p>
</blockquote>
<hr>
<h2 id="에러-처리-error-handling">에러 처리 (Error Handling)</h2>
<p>프로그램이 실행되는 동안 에러 발생 처리를 위해 에러 처리를 사용한다.
이러한 에러 처리 방법에는 여러 가지가 존재 한다.</p>
<p>함수 선언에서 <code>throws</code> 키워드를 붙여서 해당 함수에서 에러가 발생 할 수 있음을 나타낸다.
에러가 발생할 수 있는 함수를 호출 할 때는 표현식 앞에 <code>try</code>를 붙여서 나타낸다.
<code>catch</code> 절에 의해 처리될 때까지 현재 범위에서 에러를 자동으로 보낸다.</p>
<pre><code class="language-swift">func canThrowAnError() throws {
    ...
}

do {
    try canThrowAnError()
} catch {
    print(&quot;ERROR: \(error)&quot;)
}</code></pre>
<p>이때 <code>do</code>구문은 에러를 하나이상의 <code>catch</code>에게 보낼 수 있는 범위를 만든다. </p>
<blockquote>
<p>에러처리는 직접 사용을 해보면서 익히는게 제일 빠르며 후에 에러처리 관련해서 자세한 포스트를 작성하면서 조금더 많은 사용법을 작성할 예정이다.</p>
</blockquote>
<hr>
<h2 id="역설과-전제조건">역설과 전제조건</h2>
<blockquote>
<p>약간 이건 부끄러운 말이지만 해당 용어에 대해서 필자는 처음 들어봤다.</p>
</blockquote>
<p>런타임시 발생하는 조건이다. 추가 코드 실행 전 이를 사용해 필수조건이 충족되는지 확인할 수 있다.</p>
<p><code>역설</code>은 개발과정에서 실수와 잘못된 가정을 찾는데 도움이 되고
<code>전제조건</code>은 프로덕션 문제를 감지하는데 도움이 된다.</p>
<p>역설과 전제조건을 사용하는 것은 유효하지 않는 조건이 발생하지 않게 코드를 디자인하기 위함이다.</p>
<blockquote>
<p>뭔가 사용은 해본거 같은데 하지 않아서 이 부분에 대해서는 요약이 불가능해서 이건 이 정도로 줄이고 후에 알게 되거나 사용을 했다면 다른 포스트로 찾아오도록 하겠다.</p>
</blockquote>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
<ul>
<li>다시 이렇게 처음부터 Swift 공부를 하고 있으니 은근 재밌는게 많이 보이고 안쓰던 방식으로 다른 타입들을 사용하는 방법도 다시 알게 되었다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Swift 기초 공부 - 1 : 둘러보기]]></title>
            <link>https://velog.io/@sean_kk/Swift-Swift-%EA%B8%B0%EC%B4%88-%EA%B3%B5%EB%B6%80-1</link>
            <guid>https://velog.io/@sean_kk/Swift-Swift-%EA%B8%B0%EC%B4%88-%EA%B3%B5%EB%B6%80-1</guid>
            <pubDate>Tue, 23 May 2023 06:10:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<blockquote>
<h4 id="참고자료--swiftorg">참고자료 : <a href="https://www.swift.org/">swift.org</a></h4>
</blockquote>
<h1 id="시작">시작</h1>
<h3 id="시작글">시작글</h3>
<ul>
<li><p>현재 나는 Objective-C 와 Swift 언어로 iOS앱의 개발을 할 수 있지만 뭔가가 부족한 느낌을 항상 받았다.
거진 다 아는 내용이지만 머리로는 알겠는데 말로 하거나 조금만 심화로 Deep 하게 들어가면 부족한 것을 너무 절실하게 느꼈다.
그래서 기초가 조금 더 탄탄했으면 했는데 그 기초를 다시 공부하면서 조금 더 탄탄하게 쌓을 생각이다.</p>
</li>
<li><p>당연하게도 이 글은 기초 공부지만 어느정도 swift를 아는 놈이 작성하는거라 문서를 보고 하지만 약간 야매같은 느낌으로 이 글을 딱딱하게 책을 읽듯 요약하는게 아니라 나만의 방식으로 유연한 요약을 목표로 할 생각이다.</p>
</li>
</ul>
<h1 id="분석">분석</h1>
<h2 id="welcome-to-swift">Welcome to Swift</h2>
<h3 id="what-is-swift">What is Swift?</h3>
<p>swift는 문서에 따르면 새로 배우기 쉽고 표현하기 좋으며 재밌는 언어다.
또한 최신 프로그래밍 패턴을 채택해 많은 클래스의 일반적 오류를 정의한다.</p>
<ul>
<li>변수는 항상 사용 전 항상 초기화 되어야 한다.</li>
<li>Array의 인덱스는 범위 초과 에러에 대해 검사해야 한다.</li>
<li>정수는 오버플로우 검사되어야 한다.</li>
<li>옵셔널은 nil값이 명시적 처리되도록 한다.</li>
<li>메모리는 자동으로 관리된다.</li>
<li>에러 처리 통해 예기치 않은 오류 처리 가능하다.</li>
</ul>
<p>강력한 타입 추론과 패턴 매칭을 통해 간결한 표현이 가능하다.</p>
<blockquote>
<p>결국에는 Swift에 대한 자랑이라서 중요하다 싶은 부분만 요약하고 넘어가겠다.</p>
</blockquote>
<h3 id="a-swift-tour">A Swift Tour</h3>
<p>swift는 입력/출력 또는 문자열 처리와 같은 기능을 위한 별도의 라이브러리가 필요 없다.
전역 범위로 작성한 코드는 전체에서 사용되기에 <code>main()</code>이 필요 없다.
모든 구문의 끝에는 세미콜론도 필요 없다.
해당 부분에서는 swift에 대한 간단한 코드 작성 방법에 대해서 알려준다.</p>
<blockquote>
<p>swift는 main() 필요없고 세미콜론(;)도 굳이 붙일 필요 없음</p>
</blockquote>
<hr>
<h4 id="간단한-값">간단한 값</h4>
<p><code>상수 선언 = let / 변수 선언 = var</code></p>
<p>항상 타입을 명시해야 하는것은 아니다. 값을 제공하면 컴파일러는 타입을 유추한다. 하지만 초기값이 충분한 정보를 제공하지 않거나 없는 경우 뒤 콜론(:)으로 구분해 타입을 지정한다.</p>
<pre><code class="language-swift">let intNumber: Int = 30</code></pre>
<p>만들어진 값은 다른 타입의 값으로 절대 변경되지 않는다.
값을 다른 타입으로 변경해야 하면 원하는 타입의 인스턴스를 만들어야 한다.</p>
<p>Int에서 String로 변환하는 경우에는 다음과 같이 할 수 있다.</p>
<pre><code class="language-swift">let intNumber: Int = 10
let strText: String = &quot;Test Text&quot;
let numberText: String = strText + String(intNumber)</code></pre>
<p>이렇게 작성을 하는 방법도 있지만 조금 더 쉬운 방법도 존재</p>
<pre><code class="language-swift">let intNumber: Int = 10
let numberText: String = &quot;Test Text \(intNumber)&quot;</code></pre>
<p>이렇게 작성을 하게 되면 두 코드는 똑같은 값을 내보내게 될 것이다.
또한 <code>\()</code> 안에 인스턴스가 들어가기에 단순 변수나 상수 뿐 아니라 내부에서 <code>특정한 동작</code>을 할 수 있다.</p>
<p>String 에서 여러 문자열을 입력하고 싶으면 <code>&quot;&quot;&quot;</code> 를 사용해서 작성한다.</p>
<p><code>대괄호 [ ]</code>를 사용하여 배열(Array) 딕셔너리(Dictionary)를 생성할 수 있다.</p>
<pre><code class="language-swift">let arrayTest: [String] = [&quot;A&quot;,&quot;B&quot;,&quot;C&quot;]
let dictTest: Dictionary&lt;String, String&gt; = [&quot;A&quot;:&quot;a&quot;,&quot;B&quot;:&quot;b&quot;]</code></pre>
<p>요소를 추가함에 따라 자동으로 그 크기는 증가한다.
변수와 마찬가지로 <code>빈 값</code>으로 선언도 가능하다.</p>
<pre><code class="language-swift">var arrayEmpty: [String] = []
var dictEmpty: [Sting: String] = [:]</code></pre>
<p><code>빈 값</code>으로 배열과 딕셔너리를 할당하려면 그 <code>타입을 명시해줘야</code> 한다.
Dictionary의 타입 명시 방법은 위와 아래 방법 편한거로 사용하면 된다.</p>
<blockquote>
<p>타입 추론이 참으로 좋은 방법이긴 하고 편하긴 하지만 개인적으로 나는 모든 타입의 선언시에 전부 타입을 명시하는 편이긴하다.
|
속도 면에서도 그렇지만 유지보수를 위해 단순히 변수만 보고 이 변수가 어떠한 타입인지 바로바로 알 수 있다는 큰 장점이 있기 때문이다.
|
여러 타입에 대해서는 후에 더 자세하게 설명이 될 예정이다.</p>
</blockquote>
<hr>
<h4 id="제어-흐름">제어 흐름</h4>
<p>조건문</p>
<ul>
<li>조건문을 둘러싼 소괄호는 선택 사항이나 중괄호는 필수 사항이다.<pre><code class="language-swift">if (조건) { 
  ... 
} else if (조건2) {
  ... 
} else {
  ...
}
</code></pre>
</li>
</ul>
<p>switch (조건) {
case (요소1):
    ...
case (요소2):
    ...
default:
    ...</p>
<pre><code>
반복문
- 반복문을 둘러싼 소괄호는 선택사항이나 중괄호는 필수 사항이다.
- 가장 근처의 반복문을 탈출하기 위해서는 `break`를 사용한다.
```swift
for i in 0 ..&lt; 5 {
    ...
}

while i &lt; 10 {
    ...
}

repeat {
    ...
} while i &lt; 10</code></pre><blockquote>
<p>조건문과 반복문에 대해서는 조금 더 심화 과정이 있으나 해당 부분에서는 단순히 이런게 있다는 느낌으로 진행이 되기에 조금 심화 부분은 후에 해당 부분에서 작성할 예정이다.</p>
</blockquote>
<hr>
<h4 id="함수와-클로저">함수와 클로저</h4>
<p>함수</p>
<ul>
<li>함수를 선언하기 위해서는 <code>func</code>을 사용한다.</li>
<li>소괄호 안에 인수의 리스트를 입력하고 반환 타입은 파라미터와 구분을 위해서 <code>-&gt;</code> 이후에 작성한다<pre><code class="language-swift">func testMakeFunc(first: Int) -&gt; Int {
  return 1
}</code></pre>
<blockquote>
<p>매개변수 vs 인수
인수는 해당 함수를 호출하여 데이터를 <code>전달하는 값</code>을 인수(argument)라고 하는거고 함수를 만들때 함수의 선언부에 들어가는 즉 <code>전달된 값을 받는</code> 변수를 매개변수(parameter)라고 한다.
|
요거는 용어적인 구분인데 이정도는 제대로 구분을 하고 있는게 좋을 듯 하여 이렇게 따로 작성을 하였다.</p>
</blockquote>
</li>
</ul>
<p>함수는 1급 타입 = first-class type 으로 이것은 함수가 다른 함수를 값으로 반환할 수 있다는 뜻이다.
또한 다른 함수를 인수로 받을 수 있다.</p>
<pre><code class="language-swift">func testFunc(first: (Int) -&gt; Bool) -&gt; ((Int) -&gt; Bool) {
    func addTest(number: Int) -&gt; Bool {
        if number == 1 {
            return true
        }
    }

    if first {
        return addTest(number: 1)
    } else {
        return addTest(number: 2)
    }
}

func checkResult(number: Int) -&gt; Bool {
    if number == 1 {
        return true
    }
    return false
}

testFunc(first: checkResult)</code></pre>
<blockquote>
<p>함수는 거의 이게 전부라고 보면 될 정도로 그 사용이 복잡하지는 않다. 하지만 조금 심화되는 부분이 있기에 해당 부분에 대해서는 함수 파트에서 따로 작성할 예정이다.</p>
</blockquote>
<p> 클로저</p>
<blockquote>
<p>클로저에 대해서는 함수의 일종이라 보면 된다.
해당 부분은 이곳에서 짧게 다룰바에 그냥 클로저만 따로 다루는게 맞다 생각해 이 부분은 넘기도록 하겠다.</p>
</blockquote>
<hr>
<h4 id="객체와-클래스-object-and-class">객체와 클래스 (Object and Class)</h4>
<p>클래스</p>
<ul>
<li>클래스 내에서 프로퍼티의 선언은 클래스 내에 있다는 점을 제외하고는 모두 동일하다.</li>
<li>클래스 이름 뒤에 <code>소괄호 ( )</code>를 넣어 클래스의 인스턴스를 생성할 수 있으며 인스턴스의 프로퍼티와 메서드에 접근하기 위해서는 <code>점 .</code> 구문을 사용한다.</li>
</ul>
<blockquote>
<p>클래스를 생성할 때 내부의 프로퍼티라던가 init, getter, setter, self, 등 여러 부분에 대해서는 후에 클래스와 관련된 부분에서 조금 더 자세하게 작성할 예정이다.</p>
</blockquote>
<hr>
<h4 id="열거형과-구조체-enumerations-and-structres">열거형과 구조체 (Enumerations and Structres)</h4>
<p>열거형</p>
<ul>
<li>열거형 생성을 위해서 <code>enum</code>을 사용한다. 열거형도 메서드를 가질 수 있다.</li>
</ul>
<p>구조체</p>
<ul>
<li>구조체 생성을 위해서 <code>struct</code>을 사용한다.</li>
<li>구조체는 메서드와 초기화 구문을 포함해 클래스와 동일 한 동작을 많이 지원한다.</li>
</ul>
<blockquote>
<p>열거형과 구조체의 좀 더 자세한 활용 방법은 뒤에서 다룰 예정이다</p>
</blockquote>
<blockquote>
<p>구조체와 클래스
이 두 타입은 매우 비슷한 동작을 지원한다.
하지만 둘의 가장 큰 차이점은 구조체는 항상 복사되어 값으로 전달이 되지만 클래스의 경우에는 참조로 전달이 된다는 차이가 있다.
|
해당 내용에 대해서는 구조체 부분에서 조금더 자세히 다룰 예정이다.</p>
</blockquote>
<hr>
<h4 id="동시성">동시성</h4>
<p>비동기적 실행 함수를 나타내기 위해서 <code>async</code>를 사용한다.</p>
<pre><code class="language-swift">func asyncFunc() async -&gt; Int {
    return 1
}</code></pre>
<p>앞에 <code>await</code>를 작성하여 비동기 함수를 호출하는 것을 나타낸다.</p>
<pre><code class="language-swift">let asyncLet = await asyncFunc()</code></pre>
<p>비동기 함수 호출 위해 <code>asnyc let</code> 을 사용해 다른 비동기 코드와 병렬 실행이 가능하다. <code>await</code> 를 사용해 반환값을 사용한다.</p>
<p>비동기 함수의 반환을 기다리지 않고 동기 코드에서 비동기 함수 호출 하려면 <code>Task</code>를 사용한다.</p>
<pre><code class="language-swift">func asyncLetFunc() async {
    async let number = asyncFunc()
}

Task {
    await asyncLetFunc()
}</code></pre>
<blockquote>
<p>이 부분은 이렇게 글로 요약 된걸 봐봤자 잘 모른다.
그냥 동기와 비동기가 무슨 차이인지 둘을 왜 구분을 해서 사용을 해야 하는건지 그런거만 잘 생각해보고 자세한 내용은 후에 확인을 하도록 하자</p>
</blockquote>
<hr>
<h4 id="프로토콜과-확장-protocol-and-extensions">프로토콜과 확장 (Protocol and Extensions)</h4>
<p>프로토콜</p>
<ul>
<li>프로토콜 선언을 위해서는 <code>protocol</code>을 사용한다.</li>
<li>클래스, 열거형, 그리고 구조체는 프로토콜을 채택할 수 있다.</li>
</ul>
<p>확장</p>
<ul>
<li>새로운 메서드와 계산된 프로퍼티와 같이 존재하는 타입에 기능을 추가하려면 <code>확장 (extension)</code>을 사용한다.</li>
<li>확장을 사용하여 다른 곳에서 선언된 타입이나 라이브러리 등 여러 타입에 프로토콜 준수를 추가 할 수 있다.</li>
</ul>
<blockquote>
<p>프로토콜과 확장
이 두 가지는 매우 중요하고 자주 쓰이는 부분이다.
자세한 활용 방법은 뒤에서 다시 작성하겠다.</p>
</blockquote>
<hr>
<h4 id="에러-처리">에러 처리</h4>
<p>에러를 던지기 위해서 <code>throw</code>를 사용하고 에러를 던질 수 있는 함수를 나타내기 위해서 <code>throws</code>를 사용한다.
함수에서 에러가 발생하면 함수는 즉시 반환된고 호출한 코드가 에러를 처리한다.</p>
<pre><code class="language-swift">func errorThrowFunc() throw {
    ...
}</code></pre>
<p>에러를 처리하는 방법은 몇가지가 존재 하는데 하나의 방법으로는 <code>do-catch</code> 를 사용하는 것이다.
<code>do 블럭</code> 내에서 앞에 <code>try</code>를 작성해 에러가 발생할 수 있는 코드임을 표시한다.</p>
<pre><code class="language-swift">func errorDoCatchFunc() {
    do {
        let check = try errorThrowFunc()
    } catch {
        print(&quot;ERROR: \(error)&quot;)
    }
}</code></pre>
<p>catch 블럭 내에서 에러는 다른 이름으로 지정하기 전까지는 <code>error</code> 이라는 이름으로 주어진다.</p>
<p>특정 에러를 처리하는 여러개의 catch 블럭을 제공할 수 있으며 switch 에서 하는 것처럼 사용하면 된다.</p>
<blockquote>
<p>해당 부분을 자세하게 다루는 방법은 후에 조금 더 자세하게 설명하도록 하겠다.</p>
</blockquote>
<hr>
<h4 id="제너릭">제너릭</h4>
<p>제너릭은 <code>꺾쇠 &lt; &gt;</code>안에 이름을 작성해서 생성한다.
제너릭은 함수와 메서드 뿐 아니라 클래스와 열거형, 구조체도 만들 수 있다.</p>
<pre><code class="language-swift">func makeGenericFunc&lt;T&gt;(num: T) -&gt; T {
    return num
}

enum GenericEnum&lt;T&gt; {
    case none
    case some(T)
}</code></pre>
<p>요구사항의 리스트들을 지정하기 위해서는 본문 바로 전에 <code>where</code>을 사용해야 한다. 타입이 프로토콜을 구현하도록 요구하거나 클래스에 특정 상위 클래스가 있어야 한다든가 하는 요구를 필요로 할 때 말이다.</p>
<blockquote>
<p>제너릭도 잘 활용하면 아주 유용한 도구가 되므로 지금은 이런게 있다는것만 알아두면 된다.</p>
</blockquote>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
<ul>
<li>엄청나게 주관적으로 요약을 해서 작성을 하긴 했는데 지금 이 포스트에서는 그냥 이런게 있구나 하는 정도로만 넘어가면 될듯 하여 이렇게 작성하였다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[iOS] Notification Push 사용 - 4 : Push 확인]]></title>
            <link>https://velog.io/@sean_kk/iOS-Notification-Push-%EC%82%AC%EC%9A%A9-4-Push-%ED%99%95%EC%9D%B8</link>
            <guid>https://velog.io/@sean_kk/iOS-Notification-Push-%EC%82%AC%EC%9A%A9-4-Push-%ED%99%95%EC%9D%B8</guid>
            <pubDate>Tue, 23 May 2023 01:34:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>누군가에게 알려주기 보다는 나 스스로 정리 하며 언젠가 다시 사용할 때를 대비하는 글을 작성할것이다.</p>
</blockquote>
<h1 id="시작">시작</h1>
<h3 id="시작글">시작글</h3>
<ul>
<li><p>해당 포스트는 3편까지 진행된 상태의 프로젝트로 진행을 하기에 관련 프로젝트 작성된게 없다면 <a href="https://velog.io/@sean_kk/iOS-Notification-Push-%EC%82%AC%EC%9A%A9-3-Firebase-%EC%97%B0%EB%8F%99-FCM">3편</a>을 한 번 보는걸 추천한다.</p>
</li>
<li><p>FCM으로 직접받으면서 하면 좋겠지만 FCM은 push가 오는게 느려 3편의 마지막 번외에서 이야기 했던 <code>푸시 알림-테스터</code> 프로그램을 사용하여 진행을 할 예정이다.</p>
</li>
<li><p>즉, 해당 방법은 테스터로만 하는것이기에 혹시나 다른곳에서 다른 방법을 쓴다면 그 방법대로 보는것이 맞다.</p>
</li>
</ul>
<h1 id="분석">분석</h1>
<p>다음과 같은 내용의 Push를 테스터를 통해 앱에 직접 보낸다.
<img src="https://velog.velcdn.com/images/sean_kk/post/b05c938e-93d9-486d-9b2c-ff659e7401d7/image.png" alt=""></p>
<p>push를 클릭하게 될 경우 동작할 코드를 <code>userNotificationCenter()</code> 해당 함수에 작성한다.</p>
<pre><code class="language-swift">func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
    print(&quot;In_UserNotification&quot;)

    let userInfo = response.notification.request.content.userInfo
    print(&quot;userInfo: \(userInfo)&quot;)
    let title = response.notification.request.content.title
    print(&quot;title: \(title)&quot;)
    let body = response.notification.request.content.body
    print(&quot;body: \(body)&quot;)

    guard let aps = userInfo[&quot;aps&quot;] as? Dictionary&lt;String, Any&gt; else { return }
    print(&quot;========== ========== ==========&quot;)
    print(aps)

    guard let alert = aps[&quot;alert&quot;] as? Dictionary&lt;String, Any&gt; else { return}
    print(&quot;========== ========== ==========&quot;)
    print(alert[&quot;sound&quot;])

    guard let url: String = userInfo[&quot;velog_URL&quot;] as? String else { return }
    print(&quot;========== ========== ==========&quot;)
    print(url)
    print(&quot;========== ========== ==========&quot;)

    if let messageID = userInfo[gcmMessageIDKey] {
        print(&quot;MessageId: \(messageID)&quot;)
    }
}</code></pre>
<p>일단 Push 로 받은 값은 userInfo에 저장을 시킨다. 해당 타입은 <code>Dictionary</code>로 [AnyHashable : Any] 의 형식을 가진다.</p>
<p>그렇기에 해당 데이터를 사용할 수 있는 타입으로 타입캐스팅 하는 과정을 거치게 된다.</p>
<p>Push되는 자료를 보면 velog_URL 부분은 string 한줄 aps 부분은 또다른 dictionary 로 구성이 되었기에 해당 부분에 맞는 타입으로 타입캐스팅을 해준다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/6c72cb9d-d2f1-47e4-957b-62e717ed21fe/image.png" alt=""></p>
<p>해당 방법 외에도 push의 title과 body에 들어가는 내용은 따로 타입 캐스팅과 같은 방법을 거치지 않고 확인 하는 방법도 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/sean_kk/post/b5595192-314f-403f-9cd0-6d2b5dc4736a/image.png" alt=""></p>
<h1 id="참고자료">참고자료</h1>
<h1 id="기타">기타</h1>
<blockquote>
<p>당연 틀린 부분 지적은 감사하나 비난은 정중하게 사양하겠다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>