<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>DevCode : IT 개발 기술 아카이브</title>
    <link>https://kimback03.tistory.com/</link>
    <description>DevCode : IT 개발 기술 아카이브</description>
    <language>ko</language>
    <pubDate>Wed, 5 Aug 2026 07:07:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코드와트</managingEditor>
    <image>
      <title>DevCode : IT 개발 기술 아카이브</title>
      <url>https://tistory1.daumcdn.net/tistory/1623834/attach/1bb57b487e1c4b3680638adb4f4d87a3</url>
      <link>https://kimback03.tistory.com</link>
    </image>
    <item>
      <title>RAG 성능 고도화 &amp;mdash; Self-RAG: 스스로 검열하는 RAG</title>
      <link>https://kimback03.tistory.com/201</link>
      <description>&lt;h1 id=&quot;rag-성능-고도화--self-rag-스스로-검열하는-rag&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;1&quot;&gt;RAG 성능 고도화 &amp;mdash; Self-RAG: 스스로 검열하는 RAG&lt;/h1&gt;
&lt;blockquote style=&quot;background-color: #f0f0f0; color: #5c5c5c; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-source-line=&quot;3&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 Self-RAG를 정리한 기록이다. 검색&amp;middot;생성&amp;middot;평가의 각 단계에 LLM이 개입해 스스로 검열하는 구조를, 라이브러리 없이 작은 루프로 직접 구현해보며 짚는다. 책과 Gemini의 도움을 빌려 궁금증을 해소해가며 쌓았다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;일반-rag의-맹점--관련-없어도-일단-답을-만든다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;5&quot; data-ke-size=&quot;size26&quot;&gt;일반 RAG의 맹점 &amp;mdash; 관련 없어도 일단 답을 만든다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;검색 알고리즘에 리랭킹까지 얹으면 RAG 성능은 확실히 올라간다. 거기까지는 앞에서 정리한 그대로다. 그런데 이 모든 걸 다 적용해도 끝까지 남는 구조적인 약점이 하나 있다. 전통적인 RAG는 사용자가 문서와 아무 상관 없는 질문을 던져도, 어떻게든 뭔가를 찾아서 답을 만들어내려고 한다는 점이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;일단 가장 비슷해 보이는 문서를 끌어오고, 생성 모델은 그 엉뚱한 자료를 재료 삼아 그럴듯한 문장을 짜낸다. 검색해온 정보를 제대로 활용하지 못하거나, 답을 만드는 동안 원래 질문의 맥락을 잃어버리는 경우도 흔하다. 결국 이 모든 길이 한 곳으로 모인다. '할루시네이션', 즉 모델이 사실처럼 지어낸 거짓 답변이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;그러니까 검색을 더 잘하게 만드는 것만으로는 부족하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;12&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-source-line=&quot;12&quot;&gt;검색이 필요한지 아닌지를 먼저 판단하고&lt;/li&gt;
&lt;li data-source-line=&quot;13&quot;&gt;가져온 자료가 쓸 만한지 따지고&lt;/li&gt;
&lt;li data-source-line=&quot;14&quot;&gt;자기가 쓴 답이 근거가 있는지까지 스스로 점검하는 단계가 있어야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;그 발상을 시스템으로 구현한 게 Self-RAG다.&lt;/p&gt;
&lt;h2 id=&quot;self-rag--모델이-자기-작업을-검열하게-만든다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;18&quot; data-ke-size=&quot;size26&quot;&gt;Self-RAG &amp;mdash; 모델이 자기 작업을 검열하게 만든다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;Self-RAG(Self-Reflective Retrieval-Augmented Generation)는 이름 그대로 '자기 반성적' RAG다. 핵심은 LLM이 RAG의 각 단계에 직접 개입해서 정교하게 제어한다는 데 있다. 그냥 검색해서 답하는 게 아니라, 단계마다 모델이 특수한 토큰을 뱉어 흐름을 스스로 조종한다. 검색이 필요한지 아닌지를 판단하고(조건), 검색해온 정보가 쓸 만한지 평가하고(도구), 최종 응답의 품질까지 관리한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 무엇이 달라지는가. 필요할 때만 문서를 검색하고, 가져온 정보를 검증한 뒤에야 답을 만들기 때문에, 앞서 말한 '관련 없어도 억지로 답하는' 문제가 근본부터 줄어든다. 신뢰할 수 있는 답변만 사용자에게 내보낼 수 있게 되는 것이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;24&quot; data-ke-size=&quot;size16&quot;&gt;전체 동작은 크게 세 단계로 굴러간다. 검색, 생성, 평가다. 아래 그림이 그 흐름을 한눈에 보여준다.&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;26&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;45&quot; data-ke-size=&quot;size16&quot;&gt;이제 세 단계를 하나씩 풀어보겠다.&lt;/p&gt;
&lt;h2 id=&quot;1단계-검색--이걸-굳이-찾아봐야-하나부터-묻는다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;47&quot; data-ke-size=&quot;size26&quot;&gt;1단계 검색 &amp;mdash; &quot;이걸 굳이 찾아봐야 하나?&quot;부터 묻는다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;49&quot; data-ke-size=&quot;size16&quot;&gt;Self-RAG의 검색 단계는 단순히 데이터를 가져오는 일이 아니다. 그 앞에 '지능적인 판단'이 한 겹 깔린다. 일반적인 상황에서 RAG는 질문이 들어오면 묻지도 따지지도 않고 DB를 뒤지지만, Self-RAG는 그 전에 &quot;내가 이걸 검색해야 할까?&quot;를 먼저 고민한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;51&quot; data-ke-size=&quot;size16&quot;&gt;이 판단을 가능하게 하는 게 일명 반성 토큰(Reflection Tokens)이다. 모델이 학습 단계에서 익히는 특수 토큰들인데, 그중 검색 여부를 결정하는 첫 번째 토큰이&lt;span&gt;&amp;nbsp;&lt;/span&gt;[Retrieve]다. 모델은 들어온 질문을 보고 &quot;내 내부 지식만으로 충분한가?&quot;를 스스로 평가한 뒤, 셋 중 하나를 뱉는다. 외부 지식이 꼭 필요하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;[Retrieve]로 검색을 요청하고, 상식 수준이면&lt;span&gt;&amp;nbsp;&lt;/span&gt;[No Retrieval]로 바로 답하며, 흐름상 검색 없이 이어가도 되면&lt;span&gt;&amp;nbsp;&lt;/span&gt;[Continue]를 낸다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;53&quot; data-ke-size=&quot;size16&quot;&gt;이 분석 단계가 왜 중요할까. 모든 질문에 무조건 검색을 거는 건 비효율적인 데다 위험하기 때문이다. &quot;안녕?&quot;, &quot;오늘 날씨 어때?&quot; 같은 단순한 대화에 굳이 어려운 기술 문서를 끌어다 붙이면 오히려 답변이 꼬인다. 불필요한 노이즈가 환각을 유발하는 것이다. 게다가 임베딩과 벡터 검색은 앞서 봤듯 자원을 잡아먹는 작업이라, 꼭 필요할 때만 검색하면 그만큼 추론 속도도 빨라지고 비용도 아낀다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;55&quot; data-ke-size=&quot;size16&quot;&gt;비유하자면 이렇다. 일반 RAG는 오픈북 시험에서 모든 문제를 일일이 교과서에서 찾아보는 학생이다. 아는 문제까지 굳이 펼쳐본다. 반면 Self-RAG는 아는 문제는 바로 풀고, 모르는 문제만 표시해뒀다가 나중에 사전을 찾는 똑똑한 학생이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;57&quot; data-ke-size=&quot;size16&quot;&gt;여기서 한 가지 자연스러운 의문이 생긴다. 검색이 필요한지 아닌지를 가르는 이 판단을, 프롬프트로 LLM에게 시키는 것인지, 아니면 Self-RAG 전용 모델이 따로 있는 것인지 하는 점이다.&lt;/p&gt;
&lt;h2 id=&quot;전용-모델이냐-프롬프트냐--두-갈래-길&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;59&quot; data-ke-size=&quot;size26&quot;&gt;전용 모델이냐, 프롬프트냐 &amp;mdash; 두 갈래 길&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;61&quot; data-ke-size=&quot;size16&quot;&gt;결론부터 말하면 둘 다 가능하다. 다만 원래 논문에서의 Self-RAG는 특정하게 훈련된 '전용 모델'을 쓰는 방식이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;selfrag/selfrag-llama-7b&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 모델이 그것인데, 답변을 생성하기 전에 스스로&lt;span&gt;&amp;nbsp;&lt;/span&gt;[Retrieve]라는 토큰을 뱉도록 아예 훈련되어 있다. 모델의 '뇌' 자체가 검색 타이밍을 알고 있는 셈이라, 별도의 복잡한 프롬프트 없이도 문맥에 따라 토큰을 알아서 낸다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;63&quot; data-ke-size=&quot;size16&quot;&gt;하지만 실무에서는 전용 모델을 직접 가져다 쓰는 게 번거롭다. 그래서 GPT-4 같은 일반 LLM에게 &quot;이 질문이 검색이 필요한지 Yes/No로 답해줘&quot;라고 판사 역할을 시키는 방식을 더 많이 쓴다. 이른바 모듈형(Modular RAG) 접근이고, LangGraph 같은 프레임워크에서 주로 이렇게 짠다. 모델을 새로 훈련시킬 필요 없이, 프롬프트만 잘 짜면 어떤 고성능 LLM이든 Self-RAG의 '판단관'으로 세울 수 있다는 게 장점이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;65&quot; data-ke-size=&quot;size16&quot;&gt;이 자기반성 루프 전체를 코드로 직접 돌려보면 감이 확실히 잡힌다. 외부 라이브러리나 전용 모델 없이, Self-RAG의 판단들 &amp;mdash; 검색 여부, 관련성(IsRel), 근거(IsSup), 유용성(IsUse), 질문 재작성 &amp;mdash; 을 규칙 기반 함수로 흉내 내 '고객지원 FAQ'에 적용한 예제다. 질문 두 개를 넣어, 하나는 단순 인사라 검색을 건너뛰고([No Retrieval]), 다른 하나는 검색 &amp;rarr; 관련 없는 문서 폐기 &amp;rarr; 생성 &amp;rarr; 자기 채점까지 밟는 과정을 단계별로 찍어준다. 아래 코드로 전체 흐름을 먼저 훑은 뒤, 이어서 생성&amp;middot;평가 단계를 하나씩 자세히 보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;background-color: #f5f5f5; color: #333333; text-align: left;&quot; data-source-line=&quot;67&quot; data-info=&quot;python {data-source-line=&amp;quot;67&amp;quot;}&quot; data-role=&quot;codeBlock&quot;&gt;&lt;code&gt;&quot;&quot;&quot;
Self-RAG 개념 예제 &amp;mdash; 고객지원(Customer Support) FAQ 도메인
핵심 흐름: 질문 &amp;rarr; [Retrieve]/[No Retrieval] 판단 &amp;rarr; 문서 관련성 평가(IsRel)
         &amp;rarr; 답변 생성 &amp;rarr; 근거 확인(IsSup) + 유용성 확인(IsUse)
         &amp;rarr; 미달 시 질문 재작성 후 재검색
모든 LLM 판단 함수는 규칙/키워드 기반으로 흉내 냄 (실제로는 LLM 판단)
&quot;&quot;&quot;

import re

# ─────────────────────────────────────────────────────────────
# 1. 사내 고객지원 FAQ 지식베이스 (인메모리 벡터DB 대역)
# ─────────────────────────────────────────────────────────────
FAQ_DOCS = [
    {
        &quot;id&quot;: &quot;doc-01&quot;,
        &quot;title&quot;: &quot;배송 정책&quot;,
        &quot;content&quot;: (
            &quot;주문 후 영업일 기준 2~3일 내 배송됩니다. &quot;
            &quot;제주&amp;middot;도서산간 지역은 추가 1~2일이 소요됩니다. &quot;
            &quot;오후 2시 이전 결제 완료 건은 당일 출고합니다.&quot;
        ),
    },
    {
        &quot;id&quot;: &quot;doc-02&quot;,
        &quot;title&quot;: &quot;반품 및 환불 정책&quot;,
        &quot;content&quot;: (
            &quot;상품 수령 후 7일 이내에 반품을 신청할 수 있습니다. &quot;
            &quot;단순 변심 반품 시 왕복 배송비(6,000원)가 고객 부담입니다. &quot;
            &quot;불량&amp;middot;오배송의 경우 전액 환불이 가능합니다.&quot;
        ),
    },
    {
        &quot;id&quot;: &quot;doc-03&quot;,
        &quot;title&quot;: &quot;회원 포인트 적립 규정&quot;,
        &quot;content&quot;: (
            &quot;구매 금액의 1%가 포인트로 적립됩니다. &quot;
            &quot;포인트는 적립일로부터 1년간 유효하며, &quot;
            &quot;5,000포인트 이상 보유 시 결제에 사용할 수 있습니다.&quot;
        ),
    },
    {
        &quot;id&quot;: &quot;doc-04&quot;,
        &quot;title&quot;: &quot;고객센터 운영 시간&quot;,
        &quot;content&quot;: (
            &quot;고객센터는 평일 오전 9시부터 오후 6시까지 운영합니다. &quot;
            &quot;주말 및 공휴일은 휴무이며, 카카오톡 채널로 문의하면 &quot;
            &quot;다음 영업일에 순차 답변드립니다.&quot;
        ),
    },
    # 질문 B 첫 번째 검색 시 의도적으로 낮은 관련성 문서가 섞이도록 추가
    {
        &quot;id&quot;: &quot;doc-05&quot;,
        &quot;title&quot;: &quot;이벤트 안내&quot;,
        &quot;content&quot;: &quot;여름 할인 이벤트: 7월 한 달간 전 상품 10% 할인 적용.&quot;,
    },
]


# ─────────────────────────────────────────────────────────────
# 2. 검색기 (키워드 기반 유사도 흉내)
# ─────────────────────────────────────────────────────────────
def retrieve_docs(query: str, top_k: int = 3) -&amp;gt; list:
    &quot;&quot;&quot;
    실제로는 임베딩 벡터 유사도 검색(FAISS, Chroma 등)을 수행.
    여기서는 키워드 겹침 개수로 단순 근사.
    &quot;&quot;&quot;
    query_tokens = set(re.sub(r&quot;[^\w]&quot;, &quot; &quot;, query).split())

    scored = []
    for doc in FAQ_DOCS:
        doc_tokens = set(re.sub(r&quot;[^\w]&quot;, &quot; &quot;, doc[&quot;title&quot;] + &quot; &quot; + doc[&quot;content&quot;]).split())
        overlap = len(query_tokens &amp;amp; doc_tokens)
        scored.append((overlap, doc))

    # 점수 내림차순 정렬 후 top_k 반환
    scored.sort(key=lambda x: x[0], reverse=True)
    return [doc for _, doc in scored[:top_k]]


# ─────────────────────────────────────────────────────────────
# 3. Self-RAG 판단 함수 (실제로는 LLM 판단 &amp;mdash; 여기서는 규칙 기반)
# ─────────────────────────────────────────────────────────────

# 단순 인사/감사 패턴 &amp;mdash; 검색 불필요 신호
_NO_RETRIEVAL_PATTERNS = re.compile(
    r&quot;^(안녕|고마워|감사|ㅎㅇ|잘있어|반가워|수고|안녕하세요|괜찮으세요|어떻게 지내)&quot;, re.IGNORECASE
)

def decide_retrieval(query: str) -&amp;gt; str:
    &quot;&quot;&quot;
    [Retrieve] / [No Retrieval] 결정.
    실제로는 LLM이 '내부 지식으로 충분한가?'를 판단.
    여기서는 인사&amp;middot;감사 패턴이면 [No Retrieval], 그 외는 [Retrieve].
    &quot;&quot;&quot;
    if _NO_RETRIEVAL_PATTERNS.match(query.strip()):
        return &quot;No Retrieval&quot;
    return &quot;Retrieve&quot;


def grade_relevance(query: str, doc: dict) -&amp;gt; bool:
    &quot;&quot;&quot;
    IsRel: 검색된 문서가 질문과 관련 있는지 평가.
    실제로는 LLM이 '이 문서가 질문 해결에 도움이 되는가?'를 판단.
    여기서는 질문 키워드가 문서에 1개 이상 포함되면 관련 있다고 판단.
    &quot;&quot;&quot;
    query_tokens = set(re.sub(r&quot;[^\w]&quot;, &quot; &quot;, query).split())
    doc_text = doc[&quot;title&quot;] + &quot; &quot; + doc[&quot;content&quot;]
    doc_tokens = set(re.sub(r&quot;[^\w]&quot;, &quot; &quot;, doc_text).split())
    return len(query_tokens &amp;amp; doc_tokens) &amp;gt;= 1


def generate_answer(query: str, docs: list) -&amp;gt; str:
    &quot;&quot;&quot;
    답변 생성 단계.
    실제로는 LLM이 관련 문서를 컨텍스트로 삼아 답변을 생성.
    여기서는 관련 문서 내용을 단순 연결.
    &quot;&quot;&quot;
    if not docs:
        return &quot;죄송합니다. 관련 정보를 찾지 못했습니다.&quot;
    context = &quot; / &quot;.join(doc[&quot;content&quot;] for doc in docs)
    return f&quot;[생성된 답변] 질문 '{query}'에 대한 안내: {context}&quot;


def check_is_sup(answer: str, docs: list) -&amp;gt; bool:
    &quot;&quot;&quot;
    IsSup: 답변 내용이 검색 문서에 근거하는지 확인 (환각 방지).
    실제로는 LLM이 '답변 문장이 컨텍스트 문서에 실제로 있는 내용인가?'를 판단.
    여기서는 답변에 문서 내용 일부가 포함되어 있으면 Supported로 간주.
    &quot;&quot;&quot;
    for doc in docs:
        # 문서 내용의 첫 15자 이상이 답변에 포함되면 근거 있다고 판단
        snippet = doc[&quot;content&quot;][:15]
        if snippet in answer:
            return True
    return False


def check_is_use(query: str, answer: str) -&amp;gt; bool:
    &quot;&quot;&quot;
    IsUse: 답변이 질문에 실제로 유용한지 확인 (동문서답 방지).
    실제로는 LLM이 1~5점 척도로 유용성을 채점.
    여기서는 질문 키워드가 답변에 1개 이상 포함되면 유용하다고 판단.
    &quot;&quot;&quot;
    query_tokens = set(re.sub(r&quot;[^\w]&quot;, &quot; &quot;, query).split())
    answer_tokens = set(re.sub(r&quot;[^\w]&quot;, &quot; &quot;, answer).split())
    return len(query_tokens &amp;amp; answer_tokens) &amp;gt;= 1


def rewrite_query(original_query: str) -&amp;gt; str:
    &quot;&quot;&quot;
    질문 재작성기 (Query Rewriter).
    실제로는 LLM이 검색에 더 적합한 형태로 질문을 변환.
    여기서는 &quot;정책 안내&quot; 키워드를 추가하는 단순 규칙.
    &quot;&quot;&quot;
    return original_query.strip() + &quot; 정책 안내&quot;


# ─────────────────────────────────────────────────────────────
# 4. Self-RAG 메인 루프
# ─────────────────────────────────────────────────────────────
def self_rag(query: str, max_retry: int = 1) -&amp;gt; str:
    &quot;&quot;&quot;
    Self-RAG 자기반성 루프:
    질문 &amp;rarr; 검색 필요 여부 판단 &amp;rarr; 검색 &amp;rarr; IsRel 필터
    &amp;rarr; 답변 생성 &amp;rarr; IsSup + IsUse 채점 &amp;rarr; 미달 시 재작성 후 재검색
    &quot;&quot;&quot;
    print(f&quot;\n{'='*60}&quot;)
    print(f&quot;[질문] {query}&quot;)
    print(f&quot;{'='*60}&quot;)

    # ── 단계 1: 검색 필요 여부 판단 ──────────────────────────
    retrieval_decision = decide_retrieval(query)
    print(f&quot;\n[단계 1 &amp;middot; Retrieve 판단] -&amp;gt; {retrieval_decision}&quot;)

    if retrieval_decision == &quot;No Retrieval&quot;:
        # 검색 불필요: 내부 지식(또는 고정 인삿말)으로 바로 답변
        answer = &quot;안녕하세요! 고객지원 챗봇입니다. 무엇을 도와드릴까요?&quot;
        print(f&quot;[최종 답변] {answer}&quot;)
        return answer

    # ── 단계 2~6: 검색 &amp;rarr; 평가 &amp;rarr; 생성 &amp;rarr; 채점 루프 ─────────────
    for attempt in range(max_retry + 1):
        current_query = query if attempt == 0 else rewrite_query(query)
        if attempt &amp;gt; 0:
            print(f&quot;\n[재검색 &amp;middot; 질문 재작성] -&amp;gt; '{current_query}'&quot;)

        raw_docs = retrieve_docs(current_query)
        print(f&quot;\n[단계 2 &amp;middot; 검색 결과] {len(raw_docs)}건 수신&quot;)
        for d in raw_docs:
            print(f&quot;  &amp;middot; {d['id']}: {d['title']}&quot;)

        # ── 단계 3: 관련성 평가 (IsRel) ──────────────────────
        relevant_docs = []
        print(&quot;\n[단계 3 &amp;middot; IsRel 관련성 평가]&quot;)
        for doc in raw_docs:
            is_rel = grade_relevance(current_query, doc)
            label = &quot;관련 있음 ✓&quot; if is_rel else &quot;관련 없음 ✗ (폐기)&quot;
            print(f&quot;  &amp;middot; {doc['id']} ({doc['title']}): {label}&quot;)
            if is_rel:
                relevant_docs.append(doc)

        if not relevant_docs:
            print(&quot;  -&amp;gt; 관련 문서 없음. 재검색 시도 또는 내부 지식으로 전환.&quot;)
            if attempt == max_retry:
                answer = &quot;죄송합니다. 해당 질문에 대한 관련 정보를 찾을 수 없습니다.&quot;
                print(f&quot;\n[최종 답변] {answer}&quot;)
                return answer
            continue  # 재검색 루프

        # ── 단계 4: 답변 생성 ────────────────────────────────
        answer = generate_answer(current_query, relevant_docs)
        print(f&quot;\n[단계 4 &amp;middot; 답변 생성]\n  {answer}&quot;)

        # ── 단계 5: IsSup 근거 확인 ──────────────────────────
        is_sup = check_is_sup(answer, relevant_docs)
        print(f&quot;\n[단계 5 &amp;middot; IsSup 근거 확인] -&amp;gt; {'Supported ✓' if is_sup else 'Not Supported ✗'}&quot;)

        # ── 단계 6: IsUse 유용성 확인 ────────────────────────
        is_use = check_is_use(current_query, answer)
        print(f&quot;[단계 6 &amp;middot; IsUse 유용성 확인] -&amp;gt; {'Useful ✓' if is_use else 'Not Useful ✗'}&quot;)

        if is_sup and is_use:
            print(f&quot;\n[최종 답변] 채점 통과 -&amp;gt; 답변 확정&quot;)
            print(f&quot;  {answer}&quot;)
            return answer
        else:
            print(&quot;  -&amp;gt; 채점 미달. 질문 재작성 후 재검색합니다.&quot;)
            if attempt == max_retry:
                print(&quot;  -&amp;gt; 최대 재시도 횟수 도달. 현재 답변을 반환합니다.&quot;)
                print(f&quot;\n[최종 답변] {answer}&quot;)
                return answer

    return answer


# ─────────────────────────────────────────────────────────────
# 5. 데모 실행
# ─────────────────────────────────────────────────────────────
if __name__ == &quot;__main__&quot;:
    # ── 데모 A: 일상 인사 -&amp;gt; [No Retrieval]
    self_rag(&quot;안녕하세요, 잘 지내셨나요?&quot;)

    # ── 데모 B: 지식 질문 -&amp;gt; 검색 -&amp;gt; 관련성 평가 -&amp;gt; 생성 -&amp;gt; 채점
    # '환불' 키워드: doc-02(반품&amp;middot;환불 정책)와 관련 있고,
    # doc-01(배송), doc-03(포인트)은 IsRel 필터에서 폐기됨.
    self_rag(&quot;환불 신청은 어떻게 하나요? 기간이 있나요?&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;2단계-생성--답을-쓰면서-동시에-채점한다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;317&quot; data-ke-size=&quot;size26&quot;&gt;2단계 생성 &amp;mdash; 답을 쓰면서 동시에 채점한다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;319&quot; data-ke-size=&quot;size16&quot;&gt;생성 단계는 그냥 답변을 써 내려가는 과정이 아니다. 가져온 정보가 진짜 쓸모 있는지, 그리고 자기가 쓴 글이 팩트인지를 스스로 채점하는 '자기 검열' 과정이다. 여기서 세 가지 평가 지표가 등장하는데, 하나씩 보면 각각이 다른 함정을 막고 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;321&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째는 **관련성 평가(IsRel, Is Relevant)**다. 검색기가 가져온 문서가 항상 정답은 아니다. 질문과 전혀 상관없는 '쓰레기 데이터'를 끌어오는 일이 비일비재하다. 그래서 모델이 문서를 하나씩 훑으며 &quot;이게 질문에 답하는 데 도움이 되는가?&quot;를 판별한다. 관련 있으면 답변 재료로 쓰고, 관련 없으면 과감히 버린다. 만약 모든 문서가 관련 없으면, 검색 결과에 매달리지 않고 내부 지식으로 답하거나 &quot;모르겠습니다&quot;라고 정직하게 말한다. 논문 기준으로는 이걸 사전 학습된 모델이 자동으로 처리하지만, 실무에서는 LLM을 채점관으로 세워 프롬프트로 처리한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;323&quot; data-ke-size=&quot;size16&quot;&gt;문서를 추렸으면 이제 답을 쓰는데, 여기서도 문장마다 두 가지 토큰이 꼬치꼬치 따져 묻는다. 하나는 **근거 확인(IsSup, Is Supported)**이다. &quot;내가 지금 쓴 답변이 방금 읽은 문서에 진짜로 적혀 있는 내용인가?&quot;를 묻는다. 모델이 문서를 읽어놓고 제 마음대로 내용을 지어내는 걸 막는, 곧 할루시네이션 방지 장치다. 문서에 없는 내용을 말하면 감점하거나 해당 문장을 수정한다. 다른 하나는 **유용성 확인(IsUse, Is Useful)**이다. &quot;이 답변이 사용자 질문에 정말 도움이 되는가?&quot;를 본다. 팩트는 맞아도 질문과 핀트가 어긋난 동문서답을 걸러내는 역할이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;325&quot; data-ke-size=&quot;size16&quot;&gt;여기서 전용 모델과 일반 LLM의 차이가 또 갈린다. 도메인에 특화된 Self-RAG 전용 모델을 쓰면 이 일련의 과정을 내부에서 알아서 처리한다. 실제로&lt;span&gt;&amp;nbsp;&lt;/span&gt;selfrag/selfrag-llama&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 모델은 답변을 생성할 때 텍스트만 뱉는 게 아니라, 문장 사이사이에 반성 토큰을 섞어서 내놓는다. 출력이 이런 식이다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #f0f0f0; color: #5c5c5c; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-source-line=&quot;327&quot; data-ke-size=&quot;size16&quot;&gt;[Retrieve]&lt;span&gt;&amp;nbsp;&lt;/span&gt;[IsRel:Relevant]&lt;span&gt;&amp;nbsp;&lt;/span&gt;삼성전자는 2026년 6G 상용화를 목표로...&lt;span&gt;&amp;nbsp;&lt;/span&gt;[IsSup:Supported]&lt;span&gt;&amp;nbsp;&lt;/span&gt;[IsUse:5]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;329&quot; data-ke-size=&quot;size16&quot;&gt;우리가 &quot;이게 관련 있어?&quot;라고 따로 묻지 않아도, 모델이 문장을 쓰면서 동시에&lt;span&gt;&amp;nbsp;&lt;/span&gt;[IsRel:Relevant]라는 판단을 스스로 박아 넣는다. 채점용 프롬프트 여러 개를 따로 관리할 필요 없이, 모델 하나가 생성과 검증을 한 번의 흐름 안에서 끝낸다는 게 핵심이다. 대신 이런 모델을 쓰려면 사전 훈련 비용이 든다. 일반 LLM은 편하지만 그 대가로 모든 검증 과정을 일일이 프롬프트로 짜야 한다. 어느 쪽이든 공짜는 없다는 뜻이다.&lt;/p&gt;
&lt;h2 id=&quot;3단계-평가--자기-답을-검토하고-퇴고한다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;331&quot; data-ke-size=&quot;size26&quot;&gt;3단계 평가 &amp;mdash; 자기 답을 검토하고 퇴고한다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;333&quot; data-ke-size=&quot;size16&quot;&gt;마지막 평가 단계는 이 시스템이 왜 '자기 반성적'이라 불리는지를 가장 잘 보여주는 대목이다. 답을 한 번 내놓고 끝내는 게 아니라, 자신이 쓴 글을 검토하고 퇴고하는 과정이기 때문이다. 여기서는 앞서 나온 IsSup와 IsUse가 다시 등판해 더 엄격한 기준으로 작동한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;335&quot; data-ke-size=&quot;size16&quot;&gt;**근거 평가(IsSup)**는 팩트 체크다. 모델이 아무리 유창하게 답해도 그 내용이 검색된 문서에 없으면 무효다. 생성된 답변의 각 문장이 문서에 근거하는지를 따져, 모든 문장이 근거를 갖추면 통과시키고(Fully Supported), 일부만 맞으면 수정 대상으로 돌리며(Partially Supported), 문서와 무관한 소설을 썼으면 답변을 폐기하고 다시 생성하게 한다(No Support).&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;337&quot; data-ke-size=&quot;size16&quot;&gt;**유용성 평가(IsUse)**는 품질 체크다. 팩트가 맞더라도 질문의 정답이 아닐 수 있으니, 답변이 질문을 정말로 해결해주는지를 본다. 보통 1~5점 척도로 매기는데, 완벽하게 답하면 5점, 팩트는 맞아도 딴소리면 1점이다. 점수가 임계값(예컨대 3점)보다 낮으면 검색 쿼리를 고쳐서 다시 검색 단계로 되돌아간다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;339&quot; data-ke-size=&quot;size16&quot;&gt;그래서 Self-RAG는 답을 하나만 만들지 않는다. 검색된 여러 문서 뭉치를 바탕으로 답변 초안을 여러 버전 만들고, 각 초안에 IsSup와 IsUse 점수를 매긴 뒤, 두 점수를 곱한 값(IsSup &amp;times; IsUse)이 가장 높은 답변을 골라 사용자에게 보낸다. 만약 모든 후보의 점수가 낮으면? 질문을 다시 써서(Query Rewriting) 재검색하거나, 검색 문서가 아예 쓸모없다면 모델 내부 지식만으로 답을 재구성한다. 이 '평가 &amp;rarr; 실패 시 되돌아가기'가 바로 Self-RAG를 순환 구조로 만드는 지점이다.&lt;/p&gt;
&lt;h2 id=&quot;직접-구현하려면-채점단을-통째로-짜야-한다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;341&quot; data-ke-size=&quot;size26&quot;&gt;직접 구현하려면 채점단을 통째로 짜야 한다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;343&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 보면 자연스럽게 드는 생각이 있다. LLM으로 Self-RAG를 구현하려면 직접 만들어야 할 단계가 꽤 많겠다는 것이다. 정확한 통찰이다. 일반 RAG가 시작에서 끝까지 쭉 가는 '직진 코스'라면, Self-RAG는 갈림길과 유턴이 가득한 '복잡한 회로도'에 가깝다. 직접 구현하다 보면 &quot;생각보다 노가다가 심하네?&quot; 싶은 게 지극히 정상이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;345&quot; data-ke-size=&quot;size16&quot;&gt;전용 모델 없이 일반 LLM으로 이 시스템을 세우려면, 최소한 다음 다섯 개의 독립적인 프롬프트(또는 체인)를 각각 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채점단역할&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-source-line=&quot;347&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-source-line=&quot;349&quot;&gt;
&lt;tr data-source-line=&quot;349&quot;&gt;
&lt;td&gt;라우터(Router)&lt;/td&gt;
&lt;td&gt;질문을 보고 검색할지 말지 결정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-source-line=&quot;350&quot;&gt;
&lt;td&gt;관련성 채점기(Doc Grader)&lt;/td&gt;
&lt;td&gt;가져온 문서가 진짜 쓸모 있는지 판별&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-source-line=&quot;351&quot;&gt;
&lt;td&gt;할루시네이션 채점기(Hallucination Grader)&lt;/td&gt;
&lt;td&gt;답변이 문서에 근거하는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-source-line=&quot;352&quot;&gt;
&lt;td&gt;답변 유용성 채점기(Answer Grader)&lt;/td&gt;
&lt;td&gt;질문에 제대로 답했는지 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-source-line=&quot;353&quot;&gt;
&lt;td&gt;질문 재작성기(Question Rewriter)&lt;/td&gt;
&lt;td&gt;검색 실패 시 질문을 어떻게 바꿀지 결정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;355&quot; data-ke-size=&quot;size16&quot;&gt;이 다섯을 따로 만드는 것까지는 어떻게든 한다. 진짜 문제는 이것들을 엮는 흐름에 순환이 들어간다는 점이다. 답변이 통과 못 하면 다시 생성으로, 딴소리면 질문 재작성으로 되돌아가야 하는데, 일반적인 선형 체인으로는 이 유턴을 표현하기가 까다롭다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;357&quot; data-ke-size=&quot;size16&quot;&gt;그래서 LangGraph를 쓴다. LangChain 진영도 이 문제가 만만치 않다는 걸 알고 내놓은 도구가 LangGraph인데, Self-RAG처럼 순환이 필요한 구조를 짤 때 전 세계 개발자들이 가장 많이 집어 드는 선택지다. 검색&amp;middot;생성&amp;middot;채점을 노드(함수)로 정의하고, 채점 결과에 따라 흐름이 갈라지는 조건부 엣지(conditional edge)를 걸면 된다. 채점에 통과하면 종료, 할루시네이션이면 다시 생성, 딴소리면 질문 재작성으로 &amp;mdash; 이렇게 갈림길을 코드로 깔끔하게 표현할 수 있다. 앞서 돌려본 예제가 이 순환을&lt;span&gt;&amp;nbsp;&lt;/span&gt;if와 재시도 루프로 직접 구현한 버전이라면, LangGraph는 같은 갈림길과 유턴을 그래프로 선언해 관리해주는 도구인 셈이다.&lt;/p&gt;
&lt;h2 id=&quot;고생한-만큼-통제권을-얻는다&quot; style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-source-line=&quot;359&quot; data-ke-size=&quot;size26&quot;&gt;고생한 만큼 통제권을 얻는다&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;361&quot; data-ke-size=&quot;size16&quot;&gt;직접 짤 단계가 많다는 건 뒤집어 보면 우리가 시스템을 그만큼 완벽하게 통제할 수 있다는 뜻이다. 손이 많이 가는 대가로 얻는 게 분명하다는 얘기다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-source-line=&quot;363&quot; data-ke-size=&quot;size16&quot;&gt;가장 큰 보상은 신뢰도다. 단계마다 검증을 거치니 사용자가 가짜 답변이나 엉뚱한 대답을 받을 확률이 0에 수렴한다. 비용도 아낀다. 검색이 필요 없는 질문을 앞단에서 걸러주므로 불필요한 API 호출이 줄어든다. 그리고 무엇보다, 단순히 챗봇 하나 만드는 수준을 넘어 기업용 '지식 엔진'에 가까운 품질이 나온다. Self-RAG가 손이 많이 가면서도 끝까지 거론되는 이유가 여기에 있다고 생각한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;br /&gt;RAG 마스터 레시피 - 브라이스 유 , 조경아 , 박수진 , 김재웅&lt;br /&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216240484&quot;&gt;https://product.kyobobook.co.kr/detail/S000216240484&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테디노트의 랭체인을 활용한 RAG 비법노트&lt;br /&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216574552&quot;&gt;https://product.kyobobook.co.kr/detail/S000216574552&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://kimback03.tistory.com/196&quot;&gt;https://kimback03.tistory.com/196&lt;/a&gt; [DevCode : IT 개발 기술 아카이브:티스토리]&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI개발</category>
      <category>Rag</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/201</guid>
      <comments>https://kimback03.tistory.com/201#entry201comment</comments>
      <pubDate>Sun, 5 Jul 2026 19:05:47 +0900</pubDate>
    </item>
    <item>
      <title>RAG 성능 고도화 &amp;mdash; 리랭킹: 검색 결과를 한 번 더 거른다</title>
      <link>https://kimback03.tistory.com/200</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;RAG 성능 고도화 &amp;mdash; 리랭킹: 검색 결과를 한 번 더 거른다&quot;&gt;RAG 성능 고도화 &amp;mdash; 리랭킹: 검색 결과를 한 번 더 거른다&lt;/h1&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 RAG 검색 결과의 정확도를 끌어올리는 '리랭킹(문서 후처리)'을 정리한 기록이다. 검색이 1차로 추려준 문서를 더 정교한 기준으로 다시 줄 세우는 단계 &amp;mdash; 크로스 인코더와 3단계 깔때기 전략을 다룬다. 책과 Gemini의 도움을 빌려 궁금증을 해소해가며 작성되었다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;검색이 끝이 아니다 &amp;mdash; 한 번 더 거르는 후처리&quot; data-ke-size=&quot;size26&quot;&gt;검색이 끝이 아니다 &amp;mdash; 한 번 더 거르는 후처리&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;직전 편에서 희소&amp;middot;밀집&amp;middot;앙상블로 문서를 골라내는 검색 단을 정리했다. 그런데 검색기가 뽑아준 결과를 그대로 LLM에 넘기다 보면 한 가지가 계속 걸린다. 상위에 올라온 문서가 생각보다 질문과 안 맞는 경우가 종종 섞여 있다는 점이다. 검색은 통과했는데 정작 답이 되긴 어려운 문서들이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이유는 단순하다. 앞 단의 검색 알고리즘은 본질적으로 기계적이다. 희소검색은 단어가 겹치는지만 보고, 밀집검색은 벡터 공간에서 좌표가 가까운지만 계산한다. 둘 다 빠르고 효율적이지만, 질문의 진짜 의도까지 헤아리지는 못한다. 그래서 1차로 걸러낸 문서들을 한 번 더, 이번엔 더 정교한 기준으로 다시 평가하고 순위를 바꿔주는 단계가 필요하다. 이걸&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;문서 후처리(Post Processing)&lt;/b&gt;, 그중에서도 핵심인 순위 재조정을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;리랭킹(Re-ranking)&lt;/b&gt;이라고 부른다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;왜 1차 검색만으로는 부족한가&quot; data-ke-size=&quot;size26&quot;&gt;왜 1차 검색만으로는 부족한가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 한 가지 짚고 넘어갈 게 있다. 희소&amp;middot;밀집검색 과정에는 사실 LLM이 끼어들 자리가 거의 없다. 키워드를 매칭하거나 고차원 벡터의 유사도를 계산하는 건 LLM이 할 일이 아니다. 굳이 LLM이 관여하는 부분을 찾자면 인덱싱 단계에서 임베딩 모델 API를 호출하는 정도다. 검색 자체는 철저히 수학과 통계의 영역이라고 보면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 &quot;그냥 처음부터 LLM한테 문서들을 던지고 관련도를 매기게 하면 안 되나&quot;라는 생각이 들 수 있다. 가능은 하다. 하지만 대규모 문서 전체를 LLM이 일일이 읽고 관련성을 판단하게 하면 계산 비용이 폭발하고 시간도 오래 걸린다. 수백만 건을 LLM에 통째로 넣는 건 현실적인 선택지가 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 실전에서 쓰는 방식은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;순차 적용&lt;/b&gt;이다. 먼저 빠르고 가벼운 초기 검색(희소&amp;middot;밀집)으로 후보를 빠르게 좁힌다. 그다음 좁혀진 후보군만 리랭커가 정밀하게 다시 평가한다. 빠른 1차 필터와 정확한 2차 심사를 역할 분담시키는 것이다. 이 흐름을 그림으로 보면 검색에서 리랭킹으로 어떻게 바통이 넘어가는지가 한눈에 잡힌다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;flowchart TD

A([&quot;사용자 질문&quot;]) --&amp;gt; B

  

B[&quot;1단계 Retrieval&amp;lt;br/&amp;gt;BM25 + 벡터 검색&amp;lt;br/&amp;gt;1,000만 개 전체 문서&quot;]

B --&amp;gt; C[&quot;후보 ~100개&quot;]

  

C --&amp;gt; D[&quot;2단계 Reranking&amp;lt;br/&amp;gt;크로스 인코더&amp;lt;br/&amp;gt;(BGE-Reranker 등)&amp;lt;br/&amp;gt;100개 정밀 재점수&quot;]

D --&amp;gt; E[&quot;후보 ~10개&quot;]

  

E --&amp;gt; F{&quot;중요한&amp;lt;br/&amp;gt;질문인가?&quot;}

F -- &quot;아니오&quot; --&amp;gt; G

F -- &quot;예&quot; --&amp;gt; H[&quot;3단계 Refining&amp;lt;br/&amp;gt;LLM 최종 평가&amp;middot;선별&quot;]

H --&amp;gt; I[&quot;최종 3~5개&quot;]

I --&amp;gt; G

  

G([&quot;최종 문서 반환&quot;])

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;리랭커는 맥락을 읽는다&quot; data-ke-size=&quot;size26&quot;&gt;리랭커는 맥락을 읽는다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 리랭커는 앞 단의 검색기와 뭐가 다른가. 리랭커는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;트랜스포머(Transformer)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;기반이다. 트랜스포머는 문장 안 단어들의 관계를 통째로 파악하는 신경망 구조인데, 덕분에 단순 키워드 일치나 벡터 거리를 넘어 문서 전체의 맥락과 잠재적 가치까지 평가할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;비유하자면 이렇다. 도서관에서 '투자'라는 키워드가 들어간 책 수십 권을 1차로 추려온 상황을 떠올려보자. 키워드 검색은 여기까지가 한계다. 그런데 금융 전문가가 그 책들을 직접 한 권씩 펼쳐 읽어보고 &quot;이 책이 지금 질문하는 주제에 정말 깊이 맞닿아 있나&quot;를 판단해준다면, 추려진 순서가 완전히 달라질 것이다. 리랭커가 하는 일이 딱 이 전문가의 역할이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;방법 하나: LLM에게 점수를 매기게 하기&quot; data-ke-size=&quot;size26&quot;&gt;방법 하나: LLM에게 점수를 매기게 하기&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;리랭킹을 구현하는 가장 직관적인 방법은 Claude나 GPT 같은 고성능 LLM을 그대로 심판으로 쓰는 것이다. 성능은 확실히 올라간다. 실제로 ChatGPT를 리랭킹에 투입한 연구에서, TREC&amp;middot;BEIR 같은 여러 평가 데이터셋을 기준으로 기존의 BM25, monoBERT, monoT5, Cohere Rerank 같은 모델들을 크게 앞서는 결과가 나왔다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LLM 리랭킹의 강점은 범용성이다. GPT는 애초에 범용 모델이라 추가 학습 없이도 어느 도메인의 질의든 곧바로 적용할 수 있다. 의료든 법률이든 금융이든, 따로 학습시킬 필요 없이 그냥 던지면 평가해준다. 다만 대가가 있다. 계산 비용이 많이 든다는 점이다. 문서 하나하나에 대해 LLM을 호출해 점수를 받아야 하니, 후보가 늘어날수록 비용과 시간이 그대로 따라 붙는다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;성능은 좋지만 리소스를 많이 먹는다는 이 트레이드오프를, 다음에 볼 크로스 인코더가 한 번 더 영리하게 풀어낸다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;방법 둘: 크로스 인코더로 정밀 심사하기&quot; data-ke-size=&quot;size26&quot;&gt;방법 둘: 크로스 인코더로 정밀 심사하기&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;크로스 인코더(Cross-Encoder)는 리트리버로 쓰는 바이 인코더(Bi-Encoder)보다 훨씬 느리고 비싸다. 그런데 정확도는 압도적으로 높다. 왜 그런지, 내부에서 무슨 일이 벌어지는지를 뜯어보면 이 트레이드오프가 납득이 된다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;바이 인코더와 크로스 인코더는 입력 방식부터 다르다&quot; data-ke-size=&quot;size23&quot;&gt;바이 인코더와 크로스 인코더는 입력 방식부터 다르다&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞 편에서 다룬 임베딩 방식(FAISS, Chroma 등)이 바로 바이 인코더다. 둘의 결정적 차이는 질문과 문서를 모델에 어떻게 넣느냐에 있다. 바이 인코더는 질문 따로, 문서 따로 넣어 각각을 벡터로 만든다. 그래서 질문 벡터와 문서 벡터는 서로 만나기 전까지 상대가 무슨 단어를 품고 있는지 전혀 모른다. 반면 크로스 인코더는 질문과 문서를 한 쌍으로 묶어 동시에 넣는다. 그래서 질문 단어와 문서 단어의 관계를 전부 맞대어 비교할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 차이가 속도와 정확도의 차이로 그대로 이어진다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 구분 | 바이 인코더 (일반 검색) | 크로스 인코더 (리랭킹) |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;|---|---|---|&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 입력 방식 | 질문과 문서를 따로 넣음 | 질문과 문서를 한 쌍으로 동시에 넣음 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 상호작용 | 질문&amp;middot;문서 단어끼리 서로 모름 | 질문 단어와 문서 단어의 관계를 전부 비교 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 속도 | 매우 빠름 (미리 만든 벡터끼리 계산) | 매우 느림 (매번 모델을 통째로 돌려야 함) |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 정확도 | 보통 (문맥을 대략 파악) | 매우 높음 (관계를 정밀하게 따짐) |&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;정확도의 비밀은 어텐션이다&quot; data-ke-size=&quot;size23&quot;&gt;정확도의 비밀은 어텐션이다&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;크로스 인코더가 더 정확한 진짜 이유는 어텐션에 있다. 바이 인코더부터 보자. 질문은 질문대로 숫자가 되고, 문서는 문서대로 숫자가 된다. 둘이 만나기 전까지는 서로 안에 무슨 단어가 들었는지 모른 채 &quot;내 좌표는 여기야&quot;라고만 말하는 식이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;크로스 인코더는 다르다. 질문과 문서를&lt;span&gt;&amp;nbsp;&lt;/span&gt;[CLS] 질문 [SEP] 문서&lt;span&gt;&amp;nbsp;&lt;/span&gt;형태로 하나로 합쳐 모델에 넣는다. 그러면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;어텐션(Attention)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메커니즘이 작동한다. 어텐션은 문장 안의 각 단어가 다른 단어와 얼마나 관련 있는지를 따지는 장치인데, 이 덕분에 &quot;질문의 '주식'이라는 단어가 문서의 '변동성'이라는 단어와 어떤 논리적 관계인지&quot;를 글자 단위로 꼼꼼하게 대조한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이건 마치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;서류 전형(바이 인코더)&lt;/b&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;심층 면접(크로스 인코더)&lt;/b&gt;의 차이와 같다. 서류는 빠르게 훑어 추려낼 수 있지만, 사람의 진가는 직접 마주 앉아 대화해봐야 드러난다. 크로스 인코더는 질문과 문서를 직접 대면시켜 대화를 붙이는 셈이다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;코드로 보는 리랭킹: 1차 검색 vs 정밀 재점수&quot; data-ke-size=&quot;size23&quot;&gt;코드로 보는 리랭킹: 1차 검색 vs 정밀 재점수&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;말로 풀어낸 리랭킹을 코드로 직접 확인해보자. 외부 라이브러리나 실제 BERT 없이, 핵심 개념만 추려 '논문 검색'을 예로 짜봤다. 먼저 단순 키워드 빈도로 후보를 빠르게 추리고(1차 검색), 그다음 질문과 문서를 함께 보는 정밀 점수기로 그 후보만 다시 줄 세운다(리랭킹). 일부러 키워드만 잔뜩 겹치는 '노이즈 문서'를 섞어, 1차에서 상위였다가 리랭킹 후 밀려나는 장면을 만들었다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;&quot;&quot;&quot;

논문 검색 리랭킹 예제

- 1차 검색: TF(단순 키워드 빈도)로 후보 top-N을 빠르게 추림

- 2차 리랭킹: 크로스 인코더 흉내 &amp;mdash; 질문-문서 쌍을 함께 보는 정밀 점수 함수

(실제로는 CrossEncoder 모델 또는 LLM API 호출로 대체)

- 1차 순위 vs 리랭킹 후 순위를 나란히 출력해 순위 변동을 확인

- 노이즈 문서(전기 변압기)가 1차에서 상위권이었다가 리랭킹 후 밀려나는 사례 포함

&quot;&quot;&quot;

  

import math

import re

from typing import List, Tuple

  
  

# ──────────────────────────────────────────────

# 데이터: 논문 초록 말뭉치 (노이즈 문서 의도적으로 포함)

# ──────────────────────────────────────────────

PAPERS = [

{

&quot;id&quot;: &quot;P1&quot;,

&quot;title&quot;: &quot;Attention Is All You Need&quot;,

&quot;abstract&quot;: (

&quot;트랜스포머(Transformer) 아키텍처를 제안한다. &quot;

&quot;어텐션(Attention) 메커니즘만으로 시퀀스 변환 태스크를 수행하며, &quot;

&quot;기계 번역에서 최고 성능을 달성했다. &quot;

&quot;인코더-디코더 구조와 멀티헤드 어텐션을 사용한다.&quot;

),

},

{

&quot;id&quot;: &quot;P2&quot;,

&quot;title&quot;: &quot;BERT: Pre-training of Deep Bidirectional Transformers&quot;,

&quot;abstract&quot;: (

&quot;양방향 트랜스포머를 사전 학습하는 BERT 모델을 제안한다. &quot;

&quot;마스크 언어 모델(MLM)과 다음 문장 예측으로 언어 표현을 학습하며, &quot;

&quot;다양한 NLP 태스크에서 파인튜닝으로 최고 성능을 달성했다. &quot;

&quot;어텐션 기반 문맥 표현을 활용한다.&quot;

),

},

{

&quot;id&quot;: &quot;P3&quot;,

&quot;title&quot;: &quot;GPT-3: Language Models are Few-Shot Learners&quot;,

&quot;abstract&quot;: (

&quot;대규모 언어 모델 GPT-3를 제안한다. &quot;

&quot;퓨샷 학습(Few-shot learning)으로 다양한 NLP 태스크를 수행하며, &quot;

&quot;트랜스포머 디코더 기반의 자기회귀 생성 모델이다. &quot;

&quot;파인튜닝 없이 프롬프트만으로 높은 성능을 달성한다.&quot;

),

},

{

&quot;id&quot;: &quot;P4&quot;,

&quot;title&quot;: &quot;RoBERTa: Robustly Optimized BERT Pretraining&quot;,

&quot;abstract&quot;: (

&quot;BERT 사전 학습을 개선한 RoBERTa를 제안한다. &quot;

&quot;더 많은 데이터, 긴 학습, 동적 마스킹으로 성능을 향상시켰다. &quot;

&quot;트랜스포머 인코더 구조를 유지하면서 다양한 NLP 벤치마크에서 &quot;

&quot;BERT를 능가하는 결과를 달성했다.&quot;

),

},

{

# 노이즈: '트랜스포머' 단어를 많이 포함하지만 전기 변압기 주제

# &amp;rarr; 1차(TF 점수)에서는 상위권이지만, 문맥을 보면 자연어 처리와 무관

&quot;id&quot;: &quot;N1&quot;,

&quot;title&quot;: &quot;High-Efficiency Power Transformer Design&quot;,

&quot;abstract&quot;: (

&quot;트랜스포머 트랜스포머 트랜스포머 설계 최적화를 다룬다. &quot;

&quot;트랜스포머 코어 재질과 트랜스포머 권선 구조를 개선하여 &quot;

&quot;전력 변환 트랜스포머의 에너지 손실을 줄이는 방법을 제안한다. &quot;

&quot;산업용 전력 공급 장치의 트랜스포머 효율을 높인다.&quot;

),

},

{

&quot;id&quot;: &quot;P5&quot;,

&quot;title&quot;: &quot;T5: Exploring the Limits of Transfer Learning with NLP&quot;,

&quot;abstract&quot;: (

&quot;텍스트-투-텍스트 트랜스포머(T5) 모델을 제안한다. &quot;

&quot;모든 NLP 태스크를 텍스트 생성 문제로 통일하여 처리한다. &quot;

&quot;대규모 사전 학습 후 다양한 언어 이해 및 생성 태스크에 적용한다. &quot;

&quot;어텐션 메커니즘 기반으로 문맥을 정밀하게 포착한다.&quot;

),

},

]

  
  

# ──────────────────────────────────────────────

# 유틸: 텍스트 토큰화 (공백/구두점 기준 분리)

# ──────────────────────────────────────────────

def tokenize(text: str) -&amp;gt; List[str]:

&quot;&quot;&quot;소문자 변환 후 단어 단위 토큰화&quot;&quot;&quot;

return re.findall(r&quot;[가-힣a-zA-Z0-9]+&quot;, text.lower())

  
  

# ──────────────────────────────────────────────

# 1차 검색: TF 기반 키워드 점수 (바이 인코더 역할)

# 질문과 문서를 '따로' 보는 방식 &amp;mdash; 빠르지만 문맥 파악 한계

# ──────────────────────────────────────────────

def first_stage_score(query: str, document: str) -&amp;gt; float:

&quot;&quot;&quot;

쿼리 토큰이 문서에 등장하는 빈도의 합을 반환.

실제로는 BM25 또는 임베딩 벡터 유사도(코사인)를 사용.

단순 빈도 기반이라 노이즈 문서(키워드 반복)를 구분하지 못한다.

&quot;&quot;&quot;

query_tokens = set(tokenize(query))

doc_tokens = tokenize(document)

score = sum(doc_tokens.count(t) for t in query_tokens)

return float(score)

  
  

def first_stage_retrieve(

query: str, papers: List[dict], top_n: int = 4

) -&amp;gt; List[Tuple[dict, float]]:

&quot;&quot;&quot;1차 검색으로 후보 top_n 논문을 점수와 함께 반환&quot;&quot;&quot;

scored = []

for paper in papers:

search_text = paper[&quot;title&quot;] + &quot; &quot; + paper[&quot;abstract&quot;]

score = first_stage_score(query, search_text)

scored.append((paper, score))

scored.sort(key=lambda x: x[1], reverse=True)

return scored[:top_n]

  
  

# ──────────────────────────────────────────────

# 2차 리랭킹: 크로스 인코더 흉내 (정밀 점수 함수)

# 질문-문서 쌍을 '함께' 보는 방식

# 실제로는 CrossEncoder 모델(sentence-transformers) 또는 LLM API를 호출

# ──────────────────────────────────────────────

  

# 자연어 처리 분야의 핵심 맥락어 목록

# 실제 크로스 인코더는 이런 규칙 없이 어텐션으로 자동 파악

NLP_CONTEXT_WORDS = {

&quot;nlp&quot;, &quot;자연어&quot;, &quot;언어&quot;, &quot;모델&quot;, &quot;학습&quot;, &quot;처리&quot;,

&quot;bert&quot;, &quot;gpt&quot;, &quot;t5&quot;, &quot;roberta&quot;, &quot;태스크&quot;, &quot;번역&quot;,

&quot;생성&quot;, &quot;인코더&quot;, &quot;디코더&quot;, &quot;파인튜닝&quot;, &quot;사전&quot;,

}

  
  

def cross_encoder_simulate(query: str, document: str) -&amp;gt; float:

&quot;&quot;&quot;

[크로스 인코더 시뮬레이션]

실제로는: model.predict([[query, document]]) 또는 LLM 관련성 점수 반환.

  

두 가지 신호를 결합:

(a) 공통 키워드 IDF 가중 점수 &amp;mdash; 희귀 단어 일치에 더 높은 점수

(b) 문맥 적합성 보너스 &amp;mdash; NLP 도메인 단어가 문서에 있으면 가산점

두 신호를 합산해 '질문-문서 쌍 관련성 점수'를 산출.

&quot;&quot;&quot;

query_tokens = tokenize(query)

doc_tokens = tokenize(document)

doc_set = set(doc_tokens)

  

total_tokens = len(doc_tokens) if doc_tokens else 1

  

# ── (a) IDF 가중 공통 키워드 점수 ──────────────────

# 단어가 드물수록(문서 내 빈도 낮을수록) 더 중요하다고 간주

# 노이즈 문서는 같은 단어를 반복해 빈도가 높아 IDF 페널티를 받음

idf_score = 0.0

for token in set(query_tokens):

freq = doc_tokens.count(token)

if freq &amp;gt; 0:

# 빈도가 높을수록 log 값이 작아져 점수가 낮아짐 (IDF 역할)

idf_score += math.log(total_tokens / (freq + 1) + 1)

  

# ── (b) 문맥 적합성 보너스: NLP 도메인 단어 존재 여부 ──────

# 크로스 인코더는 어텐션으로 이런 맥락을 자동으로 파악함

context_bonus = sum(0.8 for w in NLP_CONTEXT_WORDS if w in doc_set)

  

return idf_score + context_bonus

  
  

def rerank(

query: str, candidates: List[Tuple[dict, float]]

) -&amp;gt; List[Tuple[dict, float]]:

&quot;&quot;&quot;

크로스 인코더 점수로 후보 논문을 재정렬.

실제로는 CrossEncoder.predict() 또는 LLM 관련성 점수를 사용.

&quot;&quot;&quot;

reranked = []

for paper, _ in candidates:

text = paper[&quot;title&quot;] + &quot; &quot; + paper[&quot;abstract&quot;]

score = cross_encoder_simulate(query, text)

reranked.append((paper, score))

reranked.sort(key=lambda x: x[1], reverse=True)

return reranked

  
  

# ──────────────────────────────────────────────

# 메인 실행: 1차 순위 vs 리랭킹 후 순위 비교 출력

# ──────────────────────────────────────────────

def main():

query = &quot;트랜스포머 어텐션 메커니즘을 이용한 자연어 처리 모델&quot;

  

print(&quot;=&quot; * 65)

print(f&quot;검색 질문: {query}&quot;)

print(&quot;=&quot; * 65)

  

# 1단계: 빠른 1차 검색 (BM25/벡터 역할)

top_candidates = first_stage_retrieve(query, PAPERS, top_n=4)

  

print(&quot;\n[1차 검색 결과 &amp;mdash; 빠른 키워드 빈도 점수 순위]&quot;)

print(f&quot;{'순위':&amp;lt;4} {'ID':&amp;lt;5} {'1차점수':&amp;gt;7} 제목&quot;)

print(&quot;-&quot; * 65)

for rank, (paper, score) in enumerate(top_candidates, 1):

marker = &quot; &amp;lt;- 노이즈&quot; if paper[&quot;id&quot;] == &quot;N1&quot; else &quot;&quot;

print(f&quot;{rank:&amp;lt;4} {paper['id']:&amp;lt;5} {score:&amp;gt;7.2f} {paper['title']}{marker}&quot;)

  

# 2단계: 크로스 인코더 흉내로 재순위 (후보만 재점수)

reranked = rerank(query, top_candidates)

  

print(&quot;\n[2차 리랭킹 결과 &amp;mdash; 크로스 인코더(시뮬) 정밀 점수 순위]&quot;)

print(&quot;(실제로는 CrossEncoder.predict() 또는 LLM 관련성 점수 사용)&quot;)

print(f&quot;{'순위':&amp;lt;4} {'ID':&amp;lt;5} {'정밀점수':&amp;gt;8} 제목&quot;)

print(&quot;-&quot; * 65)

for rank, (paper, score) in enumerate(reranked, 1):

marker = &quot; &amp;lt;- 노이즈&quot; if paper[&quot;id&quot;] == &quot;N1&quot; else &quot;&quot;

print(f&quot;{rank:&amp;lt;4} {paper['id']:&amp;lt;5} {score:&amp;gt;8.3f} {paper['title']}{marker}&quot;)

  

# 순위 변동 요약

print(&quot;\n[순위 변동 요약]&quot;)

first_order = [p[&quot;id&quot;] for p, _ in top_candidates]

rerank_order = [p[&quot;id&quot;] for p, _ in reranked]

for pid in first_order:

r1 = first_order.index(pid) + 1

r2 = rerank_order.index(pid) + 1

delta = r1 - r2 # 양수면 순위 상승

if delta &amp;gt; 0:

arrow = f&quot;▲ {delta}단계 상승&quot;

elif delta &amp;lt; 0:

arrow = f&quot;▼ {abs(delta)}단계 하락&quot;

else:

arrow = &quot;변동 없음&quot;

noise_tag = &quot; [노이즈 문서]&quot; if pid == &quot;N1&quot; else &quot;&quot;

print(f&quot; {pid}{noise_tag}: 1차 {r1}위 -&amp;gt; 리랭킹 {r2}위 ({arrow})&quot;)

  
  

if __name__ == &quot;__main__&quot;:

main()

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 예제의 정밀 점수기는 질문과 문서를 '한 쌍으로 함께 본다'는 크로스 인코더의 핵심만 흉내 낸 것이다. 실제 크로스 인코더(BERT)라면 이 쌍이 들어갔을 때 내부에서 이런 일이 순서대로 벌어진다. 먼저 입력이&lt;span&gt;&amp;nbsp;&lt;/span&gt;[CLS] 질문 [SEP] 문서 [SEP]&lt;span&gt;&amp;nbsp;&lt;/span&gt;형태로 하나로 합쳐진다. 그다음 전체 어텐션이 돌아간다. 질문의 '변동성'이라는 단어가 문서의 '분산 투자'와 어떤 관계인지, '사과 파이' 같은 엉뚱한 문서와는 왜 상관이 없는지를 모든 레이어에서 동시에 비교하는 단계다. 마지막으로 문장 맨 앞의&lt;span&gt;&amp;nbsp;&lt;/span&gt;[CLS]&lt;span&gt;&amp;nbsp;&lt;/span&gt;토큰을 꺼낸다. 이 토큰은 전체 문맥을 요약하는 역할을 하는데, 모델은 이 값을 보고 &quot;이 둘은 95% 확률로 관련 있다&quot;는 식의 최종 판단을 내린다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;그럼 왜 검색이 아니라 리랭킹에만 크로스 인코더를 쓰는가&quot; data-ke-size=&quot;size23&quot;&gt;그럼 왜 검색이 아니라 리랭킹에만 크로스 인코더를 쓰는가&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 자연스럽게 의문이 든다. 그렇게 정확하면 처음부터 검색에 크로스 인코더를 쓰면 되지 않나. 문서가 100만 개인 상황을 떠올려보면 답이 나온다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;바이 인코더는 질문 하나만 임베딩한 뒤 미리 저장해둔 벡터들과 수학적으로 슥 비교하면 끝난다. 0.01초 안팎이다. 반면 크로스 인코더는 질문과 문서 100만 개를 일일이 쌍으로 묶어 BERT를 100만 번 돌려야 한다. 컴퓨터가 폭발할 일이고 몇 시간이 걸린다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 리랭킹이라는 단계가 필수가 된다. 먼저 상위 10~20개만 빠르게 거르고, 그 소수만 크로스 인코더에 넣어 진짜 1등을 가려내는 것이다. 정리하면 크로스 인코더는 질문과 문서의 상호작용을 극대화해 &quot;이 문서가 이 질문에 진짜 답이 될 확률&quot;을 0~1 사이 점수로 뱉어내는 심판관이다. 대신 연산량이 많아 GPU 자원을 많이 먹고 응답이 느리다. 실무에서는&lt;span&gt;&amp;nbsp;&lt;/span&gt;BGE-Reranker나&lt;span&gt;&amp;nbsp;&lt;/span&gt;Cohere Rerank API&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 모델이 이 역할로 많이 쓰인다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;결국 답은 3단계 깔때기다&quot; data-ke-size=&quot;size26&quot;&gt;결국 답은 3단계 깔때기다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 정리하면 두 갈래의 리랭킹 방법이 손에 잡힌다. LLM은 가장 정확하지만 비싸고, 크로스 인코더는 그보다 가볍지만 그래도 1차 검색보다는 무겁다. 그럼 &quot;정확하니까 그냥 LLM을 리랭커로 쓰면 되는 거 아니냐&quot;는 결론으로 가고 싶어진다. 하지만 100건, 1000건씩 쌓이는 실전 트래픽을 떠올려보면 곧바로 요금 폭탄과 긴 지연 시간이라는 벽에 부딪힌다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 현실적인 해법은 단계를 나눠 점진적으로 정제하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;3단계 깔때기(Three-Stage Retrieval)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;전략이다. 위로 갈수록 빠르고 싸게 많이 거르고, 아래로 갈수록 느리고 비싸지만 정확하게 추리는 구조다. 깔때기처럼 후보를 단계마다 좁혀 나간다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1차는 검색(Retrieval) 단계다. BM25와 벡터 검색으로 1,000만 개 중 100개를 빠르게 골라낸다. 가장 가볍고 가장 빠른 그물질이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2차는 리랭킹(Reranking) 단계다. 가벼운 크로스 인코더(BGE-Reranker 등)로 그 100개 중 10개를 추린다. 1차보다는 무겁지만 여전히 빠르고 저렴해서 부담 없이 돌릴 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3차는 정제(Refining) 단계다. 정말 중요한 질문일 때만 LLM을 투입해 최종 3~5개의 순위를 확정한다. 가장 정확하지만 가장 비싼 카드라, 꼭 필요한 순간에만 아껴 쓰는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 비싼 도구를 맨 마지막에, 그것도 후보가 충분히 좁혀진 다음에만 꺼낸다는 데 있다. 가장 정확한 LLM을 처음부터 1,000만 개에 들이대는 게 아니라, 빠른 검색으로 100개, 크로스 인코더로 10개까지 좁힌 뒤에야 비로소 LLM에게 맡기는 것이다. 결국 리랭킹 설계도 검색 단과 똑같은 결론으로 수렴한다. 정확도와 비용 사이에서 내 서비스의 트래픽과 질문 성격을 보고 어느 단계에 얼마를 쓸지 저울질하는 일이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;br /&gt;RAG 마스터 레시피 - 브라이스 유 , 조경아 , 박수진 , 김재웅&lt;br /&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216240484&quot;&gt;https://product.kyobobook.co.kr/detail/S000216240484&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테디노트의 랭체인을 활용한 RAG 비법노트&lt;br /&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216574552&quot;&gt;https://product.kyobobook.co.kr/detail/S000216574552&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/RAG</category>
      <category>AI</category>
      <category>Rag</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/200</guid>
      <comments>https://kimback03.tistory.com/200#entry200comment</comments>
      <pubDate>Sat, 27 Jun 2026 17:35:48 +0900</pubDate>
    </item>
    <item>
      <title>RAG 성능 고도화 &amp;mdash; 검색 알고리즘: 희소,밀집,앙상블(하이브리드)</title>
      <link>https://kimback03.tistory.com/199</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;RAG 성능 고도화 &amp;mdash; 검색 알고리즘: 희소&amp;middot;밀집&amp;middot;앙상블&quot;&gt;RAG 성능 고도화 &amp;mdash; 검색 알고리즘: 희소&amp;middot;밀집&amp;middot;앙상블&lt;/h1&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 RAG의 '검색단'을 책으로 공부하며 정리한 기록이다. 키워드로 찾는 희소검색, 뜻으로 찾는 밀집검색, 그리고 둘을 섞는 앙상블까지 &amp;mdash; 검색 알고리즘 선택이 RAG 정확도를 어떻게 가르는지 짚는다. 책과 Gemini의 도움을 빌려 궁금증을 해소해가며 쌓았다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;키워드냐 의미냐, 아니면 둘 다냐&quot; data-ke-size=&quot;size26&quot;&gt;키워드냐 의미냐, 아니면 둘 다냐&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;검색 알고리즘이라고 거창하게 부르지만, 하는 일은 단순하다. 사용자가 던진 질문과 내가 가진 참조 문서들을 비교해서 &quot;이 질문에 가장 잘 맞는 문서가 어떤 거냐&quot;를 골라내는 로직이다. 그런데 이 '잘 맞는다'를 무엇으로 판단하느냐에 따라 결과가 완전히 갈린다. 글자가 똑같이 생겼는지를 볼 수도 있고, 글자는 달라도 뜻이 통하는지를 볼 수도 있다. RAG 시스템의 정확도가 이 선택 하나에서 크게 달라지기 때문에, 검색 단을 어떻게 짜느냐가 결국 전체 품질을 좌우한다고 봐도 된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 다룰 건 세 가지다. 글자 일치를 보는 희소검색, 뜻이 통하는지를 보는 밀집검색, 그리고 이 둘을 적당히 버무리는 앙상블검색. 앞에서부터 하나씩 풀어보겠다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;희소검색 &amp;mdash; 글자가 똑같이 생겼는가&quot; data-ke-size=&quot;size26&quot;&gt;희소검색 &amp;mdash; 글자가 똑같이 생겼는가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가장 먼저 떠올릴 수 있는 방식은 &quot;질문에 들어 있는 단어가 문서에도 그대로 들어 있나&quot;를 보는 것이다. 이게 희소검색(Sparse Retrieval)이고, 대표 주자가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;BM25&lt;/b&gt;라는 키워드 기반 알고리즘이다. 질문과 문서 사이에 겹치는 키워드가 많을수록 관련성이 높다고 점수를 매긴다. 구조가 단순하고 빠르다는 게 강점이지만, 뒤집어 말하면 의미적인 유사성은 잡아내지 못한다. '자동차'로 검색하면 '차량'이라고만 적힌 문서는 그냥 못 찾는다. 글자가 다르니까.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 한국어를 쓰는 순간 여기서 한 가지 함정이 더 튀어나온다. 영어는 단어가 공백으로 깔끔하게 끊어지지만, 한국어는 그렇지가 않다(중국어는 아예 띄어쓰기가 없다). 단어 뒤에 조사가 찰싹 붙어 다니기 때문이다. 그래서 한국어로 BM25를 제대로 쓰려면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;형태소 분석기&lt;/b&gt;로 문장을 품사 단위(명사&amp;middot;동사 등)로 미리 잘라주는 전처리가 사실상 필수다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;한국어에서 형태소 분석기는 '선택'이 아니라 '필수'다&quot; data-ke-size=&quot;size23&quot;&gt;한국어에서 형태소 분석기는 '선택'이 아니라 '필수'다&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이게 왜 선택이 아니라 필수인지, 가장 간단한 예로 보여주는 게 '사과 vs 사과가' 문제다. BM25는 앞서 말했듯 &quot;글자 뭉치가 똑같이 생겼는가&quot;만 따지는 알고리즘이다. 그러니 전처리를 안 하고 그냥 띄어쓰기 기준으로 자르면 이런 일이 벌어진다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문서 A가 &quot;나는 사과를 먹었다&quot;라면 토큰은&lt;span&gt;&amp;nbsp;&lt;/span&gt;['나는', '사과를', '먹었다.']로 쪼개진다. 그런데 사용자가 &quot;사과 있어?&quot;라고 물으면 토큰은&lt;span&gt;&amp;nbsp;&lt;/span&gt;['사과']다. BM25 입장에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;사과를과&lt;span&gt;&amp;nbsp;&lt;/span&gt;사과는 글자가 다르니까&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;서로 다른 단어&lt;/b&gt;다. 결과적으로 검색에 실패하거나 점수가 형편없이 낮게 나온다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기에 Kiwi 같은 형태소 분석기를 끼우면 이야기가 달라진다. &quot;나는 사과를 먹었다&quot;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;['나', '는', '사과', '를', '먹다']로, &quot;사과 있어?&quot;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;['사과', '있다']로 분해된다. 이제&lt;span&gt;&amp;nbsp;&lt;/span&gt;사과라는 핵심 형태소가 양쪽에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;정확히 일치&lt;/b&gt;한다. 검색 성공이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;형태소 분석기가 뒤에서 해주는 일은 크게 세 가지로 정리된다. 첫째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;조사 제거&lt;/b&gt;다. '가', '이', '를', '은', '는' 같은 조사를 떼어내고 '사과', '학교', '투자' 같은 명사만 남긴다. 둘째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;어간 추출(Stemming)&lt;/b&gt;이다. '먹었다', '먹고', '먹으니'를 전부 '먹다'라는 기본형 하나로 통일한다. 셋째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;복합명사 분해&lt;/b&gt;다. '주식시장'을 '주식'과 '시장'으로 나눠두기 때문에, '주식'으로만 검색해도 '주식시장'이 들어간 문서까지 걸린다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 &quot;이럴 거면 그냥 의미를 파악하는 벡터 검색(밀집검색)만 쓰면 되는 거 아니냐&quot;는 의문이 들 수 있다. 맞는 말이다. 벡터 검색은 형태소 분석 없이도 의미를 잡는다. 그럼에도 BM25를 굳이 같이 쓰는 이유가 있다. &quot;삼성전자&quot;, &quot;iPhone 15 Pro&quot; 같은 고유명사는 의미보다 글자 그대로 정확히 일치하는 게 훨씬 중요한데, 이런 건 키워드 검색이 압도적으로 강하다. 결국 벡터 검색이 흘려보내는 '정확한 키워드'를 BM25가 받쳐주는 보완재로 쓰는 셈이다. 이게 뒤에 나올 하이브리드 전략의 씨앗이기도 하다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;밀집검색 &amp;mdash; 글자는 달라도 뜻이 통하는가&quot; data-ke-size=&quot;size26&quot;&gt;밀집검색 &amp;mdash; 글자는 달라도 뜻이 통하는가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;희소검색의 한계가 명확해졌으니, 자연스럽게 반대편 접근이 필요해진다. 글자가 아니라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;뜻&lt;/b&gt;으로 비교하는 방식, 그게 밀집검색(Dense Retrieval)이다. 질문과 문서를 둘 다 고차원 벡터 공간에 임베딩(숫자 좌표로 변환)해서, 그 좌표가 얼마나 가까운지로 관련성을 따진다. 키워드가 단 하나도 안 겹쳐도 의미가 통하면 잡아낸다. 우리가 흔히 말하는 '의미 기반 검색'의 본질이 바로 이거다. 대신 공짜는 아니어서, 임베딩을 계산하는 비용이 들고 대규모 데이터를 다룰 땐 그 부담이 만만치 않다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 방식이 실제로 어떻게 굴러가는지는 '오프라인'과 '온라인' 두 단계로 나눠 보면 깔끔하게 잡힌다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;flowchart TD

subgraph OFFLINE[&quot;오프라인 &amp;mdash; 미리 (색인 단계)&quot;]

A[&quot;문서 준비&quot;] --&amp;gt; B[&quot;청킹&amp;lt;br/&amp;gt;(문서를 작은 조각으로 분할)&quot;]

B --&amp;gt; C[&quot;임베딩 모델로 각 조각 벡터화&quot;]

C --&amp;gt; D[(&quot;Vector DB / FAISS에 색인 저장&quot;)]

end

  

subgraph ONLINE[&quot;온라인 &amp;mdash; 질문 시 (쿼리 단계)&quot;]

E[&quot;사용자 질문 입력&quot;] --&amp;gt; F[&quot;문서와 동일한 임베딩 모델로 쿼리 벡터화&quot;]

F --&amp;gt; G[&quot;고차원 공간의 좌표로 표현&quot;]

G --&amp;gt; H[&quot;저장된 문서 벡터들과 유사도 비교&quot;]

H --&amp;gt; I[&quot;가장 가까운 문서 반환&quot;]

end

  

D -- &quot;색인 완료 후 서비스 제공&quot; --&amp;gt; E

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;먼저&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;오프라인&lt;/b&gt;, 즉 질문이 들어오기 전에 미리 해두는 작업이다. 수백만 개 문서를 다룬다고 해보자. 사용자가 질문할 때마다 그 많은 문서를 그 자리에서 다 읽어 숫자로 바꾸는 건 현실적으로 불가능하다. 그래서 전처리를 미리 끝내둔다. 문서를 잘게 쪼개고(청킹), 임베딩 모델을 돌려 각 조각을 숫자 좌표로 바꾼 다음, 이 좌표들을 Vector DB (FAISS, Chroma등)에 미리 저장(색인)해둔다. 핵심은 이 과정이 질문이 들어오기 전에 전부 끝난다는 점이다. 도서관에 책을 미리 분류해서 꽂아두는 것과 똑같다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그다음이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;온라인&lt;/b&gt;, 사용자가 질문을 던지는 바로 그 순간이다. 이때 시스템은 딱 그 질문 하나만 숫자로 바꾼다. 질문이 고차원 공간(예컨대 768차원) 위의 한 점으로 찍히고, 미리 저장해둔 수백만 개의 점들 중에서 내 질문 점과 가장 가까운 녀석들을 찾아낸다. 문서 전부를 매번 질문과 함께 임베딩하지 않는 이유도 여기서 분명해진다. 매번 수백만 문서를 통째로 모델에 욱여넣으면 서버가 버티질 못한다. 그래서 무거운 문서 쪽은 미리 처리해두고, 가벼운 질문만 그때그때 처리하는 것이다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;질문은 반드시 '문서와 똑같은 임베딩 모델'에 넣어야 한다&quot; data-ke-size=&quot;size23&quot;&gt;질문은 반드시 '문서와 똑같은 임베딩 모델'에 넣어야 한다&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 절대 놓치면 안 되는 게 하나 있다. 사용자의 질문을 임베딩할 때는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;문서를 임베딩할 때 썼던 것과 똑같은 모델&lt;/b&gt;을 써야 한다. 이건 정말 중요하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이유는 임베딩 모델마다 '뇌구조'가 다르기 때문이다. 각 모델은 사전학습 단계에서 학습한 데이터셋이 제각각이라, 같은 단어를 받아도 벡터 공간의 다른 위치에 점을 찍는다. 동작 방식 자체가 다른 것이다. 문서는 A 모델 기준 좌표계에 찍어놓고 질문은 B 모델 기준 좌표계에 찍으면, 두 점은 서로 다른 지도 위에 있는 셈이라 거리를 재는 것 자체가 무의미해진다. 같은 자로 재야 비교가 되는 것과 같은 이치다. (또한 모델의 토크나이저 또한 다르다.)&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;앙상블검색 &amp;mdash; 두 방식의 장점만 섞는다&quot; data-ke-size=&quot;size26&quot;&gt;앙상블검색 &amp;mdash; 두 방식의 장점만 섞는다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 오면 결론이 자연스럽게 보인다. 희소검색은 정확한 키워드에 강하지만 뜻을 못 읽고, 밀집검색은 뜻을 읽지만 정확한 글자 매칭이 약하다. 서로의 약점이 상대의 강점이다. 그럼 둘을 적당히 섞어서 밸런스를 맞추면 되지 않을까. 그게 앙상블검색이고, 하이브리드 검색이라고도 부른다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;flowchart TD

A[&quot;사용자 질문 입력&quot;] --&amp;gt; B{&quot;병렬 검색&quot;}

B --&amp;gt; C[&quot;희소 검색&amp;lt;br/&amp;gt;(BM25 키워드 기반)&quot;]

B --&amp;gt; D[&quot;밀집 검색&amp;lt;br/&amp;gt;(벡터 의미 기반)&quot;]

C --&amp;gt; E[&quot;가중치 부여&amp;lt;br/&amp;gt;(기술&amp;middot;법률 문서면 키워드 비중&amp;uarr;)&quot;]

D --&amp;gt; F[&quot;가중치 부여&amp;lt;br/&amp;gt;(기본 0.5 : 0.5)&quot;]

E --&amp;gt; G[&quot;두 결과를 가중 결합&amp;middot;재정렬&quot;]

F --&amp;gt; G

G --&amp;gt; H[&quot;최종 문서 선별&quot;]

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 두 검색기를 동시에 돌리되, 각각에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가중치&lt;/b&gt;를 어떻게 줄지를 상황에 맞게 조절하는 데 있다. 기술 문서나 법률 문서처럼 정확한 용어 매칭이 생명인 경우에는 희소검색(키워드 기반)에 가중치를 더 실어주는 게 맞다. 용어 하나 틀리면 의미가 통째로 어긋나는 영역이니까. 반대로 일상적이고 일반적인 질문을 다루는 상황에서는 의미 기반 검색에 더 높은 비중을 두는 편이 낫다. 이 비율을 도메인 성격에 따라 손으로 조정하는 게 앙상블 설계의 핵심이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 방식을 한자리에 놓고 비교하면 차이가 더 또렷하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 구분 | 희소검색 (BM25) | 밀집검색 (벡터) | 앙상블 (하이브리드) |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;|---|---|---|---|&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 원리 | 질문&amp;middot;문서의 키워드 일치도로 점수 | 질문&amp;middot;문서를 벡터로 바꿔 의미적 거리 비교 | 두 방식을 가중치로 결합 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 장점 | 빠르고 단순, 고유명사&amp;middot;정확한 키워드에 강함 | 글자가 달라도 뜻이 통하면 포착 | 두 방식의 강점을 모두 흡수, 적중률 극대화 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 단점 | 의미적 유사성 포착 불가, 한국어는 형태소 전처리 필수 | 임베딩 계산 비용&amp;middot;자원 소모가 큼 | 두 검색기를 다 돌려야 해 구성이 무겁고 가중치 튜닝 필요 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 적합한 상황 | 고유명사&amp;middot;전문 용어가 핵심인 검색 | 표현이 다양하고 의미 위주인 일반 질문 | 키워드와 의미가 둘 다 중요한 실전 환경 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지금까지 본 희소&amp;middot;밀집&amp;middot;앙상블 세 가지는 사실 따로 노는 기법이 아니다. 코드로 보면 BM25 검색기를 만들고, 벡터 검색기를 만든 다음, 이 둘을 앙상블 검색기로 묶는 한 줄기 흐름으로 자연스럽게 이어진다. 아래 통합 예제에서 세 검색기가 같은 질문에 어떻게 다르게 반응하는지, 그리고 가중치를 어디서 조절하는지를 한눈에 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;&quot;&quot;&quot;

희소검색(BM25) vs 밀집검색(코사인 유사도) vs 앙상블 검색 비교 예제

도메인: 요리 레시피 검색

실제로는 BM25 라이브러리(rank_bm25)와 임베딩 모델(SentenceTransformers 등)을 사용한다.

여기서는 표준 라이브러리만으로 핵심 개념을 구현한다.

&quot;&quot;&quot;

  

import math

from collections import Counter

  

# ──────────────────────────────────────────────

# 검색 대상 요리 레시피 문서 코퍼스

# ──────────────────────────────────────────────

DOCS = [

# 0: 키워드 &quot;파스타&quot;가 많이 등장 &amp;rarr; BM25에 유리

{

&quot;id&quot;: 0,

&quot;title&quot;: &quot;토마토 파스타&quot;,

&quot;text&quot;: &quot;파스타 면을 삶은 뒤 토마토 소스와 버무린다. &quot;

&quot;파스타는 알덴테로 삶는 것이 중요하다. &quot;

&quot;토마토 퓨레, 올리브오일, 마늘로 소스를 만들고 파스타 위에 얹는다.&quot;,

},

# 1: &quot;파스타&quot;라는 단어 없이 의미적으로 유사 &amp;rarr; 밀집검색에 유리

{

&quot;id&quot;: 1,

&quot;title&quot;: &quot;크림 뇨키&quot;,

&quot;text&quot;: &quot;감자 반죽을 빚어 만든 작은 이탈리아 덩어리를 크림 소스에 버무린다. &quot;

&quot;이탈리아 전통 면 요리의 변형으로 부드럽고 진한 맛이 특징이다.&quot;,

},

# 2: 완전히 다른 요리

{

&quot;id&quot;: 2,

&quot;title&quot;: &quot;된장찌개&quot;,

&quot;text&quot;: &quot;두부와 애호박을 넣고 된장을 풀어 끓인다. &quot;

&quot;멸치 육수를 베이스로 사용하면 깊은 맛이 난다. 한국 전통 국물 요리다.&quot;,

},

# 3: &quot;올리브오일&quot; 키워드가 겹침

{

&quot;id&quot;: 3,

&quot;title&quot;: &quot;그리스 샐러드&quot;,

&quot;text&quot;: &quot;올리브오일과 레몬즙으로 드레싱을 만든다. &quot;

&quot;토마토, 오이, 페타 치즈를 올리브오일에 버무리면 완성이다.&quot;,

},

# 4: 의미적으로 이탈리아 면 요리에 가까움

{

&quot;id&quot;: 4,

&quot;title&quot;: &quot;라자냐&quot;,

&quot;text&quot;: &quot;납작한 밀가루 면 사이에 고기 소스와 베샤멜 소스를 켜켜이 쌓아 오븐에 굽는다. &quot;

&quot;이탈리아 오븐 요리로 파티 음식으로 인기가 많다.&quot;,

},

]

  
  

# ──────────────────────────────────────────────

# 공통 유틸: 간단한 토크나이저 (공백 분리 + 간이 조사 제거)

# 실제로는 형태소 분석기(Kiwi 등)를 사용한다

# ──────────────────────────────────────────────

def tokenize(text: str) -&amp;gt; list[str]:

&quot;&quot;&quot;공백 기준으로 토큰화하고 조사&amp;middot;구두점을 제거한다 (간이 전처리)&quot;&quot;&quot;

tokens = []

for word in text.split():

word = word.strip(&quot;.,!?&quot;)

# 간단히 마지막 한 글자가 조사성 글자면 제거 (실제는 형태소 분석기 사용)

if len(word) &amp;gt; 1 and word[-1] in &quot;은는이가을를의에서로와과&quot;:

word = word[:-1]

if word:

tokens.append(word)

return tokens

  
  

# ══════════════════════════════════════════════

# 1. 희소검색 (BM25)

# 키워드 겹침 빈도 기반으로 점수를 매긴다

# 실제로는 rank_bm25 라이브러리의 BM25Okapi를 사용한다

# ══════════════════════════════════════════════

class SimpleBM25:

&quot;&quot;&quot;BM25 Okapi 알고리즘의 간이 구현&quot;&quot;&quot;

  

def __init__(self, documents: list[str], k1: float = 1.5, b: float = 0.75):

self.k1 = k1

self.b = b

self.tokenized_docs = [tokenize(doc) for doc in documents]

self.doc_count = len(self.tokenized_docs)

self.avgdl = sum(len(d) for d in self.tokenized_docs) / self.doc_count

# 역문서빈도(IDF) 계산을 위한 DF 집계

self.df: dict[str, int] = {}

for tokens in self.tokenized_docs:

for term in set(tokens):

self.df[term] = self.df.get(term, 0) + 1

  

def _idf(self, term: str) -&amp;gt; float:

&quot;&quot;&quot;IDF: 드물게 등장하는 단어일수록 높은 가중치&quot;&quot;&quot;

df = self.df.get(term, 0)

return math.log((self.doc_count - df + 0.5) / (df + 0.5) + 1)

  

def score(self, query: str) -&amp;gt; list[float]:

&quot;&quot;&quot;쿼리에 대한 각 문서의 BM25 점수를 반환한다&quot;&quot;&quot;

query_tokens = tokenize(query)

scores = []

for tokens in self.tokenized_docs:

tf_map = Counter(tokens)

dl = len(tokens)

doc_score = 0.0

for term in query_tokens:

tf = tf_map.get(term, 0)

idf = self._idf(term)

numerator = tf * (self.k1 + 1)

denominator = tf + self.k1 * (1 - self.b + self.b * dl / self.avgdl)

doc_score += idf * (numerator / denominator)

scores.append(doc_score)

return scores

  
  

# ══════════════════════════════════════════════

# 2. 밀집검색 (코사인 유사도 기반)

# 실제로는 OpenAIEmbeddings/SentenceTransformers로 수백 차원 벡터를 만들어

# FAISS로 인덱싱한다. 여기서는 문자 2-gram TF 벡터를 간이 임베딩으로 쓴다.

# ══════════════════════════════════════════════

def char_ngram_vector(text: str, n: int = 2) -&amp;gt; dict[str, float]:

&quot;&quot;&quot;문자 2-gram 빈도를 벡터로 표현한다 (실제 임베딩 모델은 의미를 학습한 벡터를 반환)&quot;&quot;&quot;

text = text.replace(&quot; &quot;, &quot;&quot;)

ngrams = [text[i:i + n] for i in range(len(text) - n + 1)]

count = Counter(ngrams)

total = sum(count.values()) or 1

return {k: v / total for k, v in count.items()}

  
  

def cosine_similarity(vec_a: dict[str, float], vec_b: dict[str, float]) -&amp;gt; float:

&quot;&quot;&quot;두 희소 벡터의 코사인 유사도를 계산한다&quot;&quot;&quot;

common = set(vec_a) &amp;amp; set(vec_b)

dot = sum(vec_a[k] * vec_b[k] for k in common)

norm_a = math.sqrt(sum(v ** 2 for v in vec_a.values()))

norm_b = math.sqrt(sum(v ** 2 for v in vec_b.values()))

if norm_a == 0 or norm_b == 0:

return 0.0

return dot / (norm_a * norm_b)

  
  

class DenseRetriever:

&quot;&quot;&quot;간이 밀집검색기: 문자 n-gram 벡터 + 코사인 유사도&quot;&quot;&quot;

  

def __init__(self, documents: list[str]):

# 오프라인 단계: 모든 문서를 미리 벡터로 변환해 저장 (임베딩 인덱싱)

self.doc_vectors = [char_ngram_vector(doc) for doc in documents]

  

def score(self, query: str) -&amp;gt; list[float]:

&quot;&quot;&quot;온라인 단계: 쿼리만 벡터화해 저장된 벡터와 코사인 유사도 계산&quot;&quot;&quot;

query_vec = char_ngram_vector(query)

return [cosine_similarity(query_vec, dv) for dv in self.doc_vectors]

  
  

# ══════════════════════════════════════════════

# 3. 앙상블 검색 (희소 + 밀집 가중합)

# 실제로는 LangChain의 EnsembleRetriever(weights=[0.5, 0.5])를 사용한다

# ══════════════════════════════════════════════

def normalize(scores: list[float]) -&amp;gt; list[float]:

&quot;&quot;&quot;점수를 [0, 1] 범위로 정규화해 두 방식의 스케일을 맞춘다&quot;&quot;&quot;

max_s = max(scores) if scores else 1

min_s = min(scores) if scores else 0

span = max_s - min_s or 1

return [(s - min_s) / span for s in scores]

  
  

def ensemble_score(bm25_scores, dense_scores, w_bm25=0.5, w_dense=0.5):

&quot;&quot;&quot;

정규화된 두 점수를 가중합한다 (w_bm25 + w_dense = 1.0)

기술/법률 문서: w_bm25를 높임 / 일상 검색: w_dense를 높임

&quot;&quot;&quot;

norm_bm25 = normalize(bm25_scores)

norm_dense = normalize(dense_scores)

return [w_bm25 * b + w_dense * d for b, d in zip(norm_bm25, norm_dense)]

  
  

# ──────────────────────────────────────────────

# 검색 실행 및 결과 출력

# ──────────────────────────────────────────────

def print_results(label: str, scores: list[float], top_k: int = 3):

&quot;&quot;&quot;점수 내림차순으로 상위 k개 결과를 출력한다&quot;&quot;&quot;

ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True)[:top_k]

print(f&quot;\n[{label}] 상위 {top_k}개&quot;)

print(&quot;-&quot; * 50)

for rank, (doc_id, score) in enumerate(ranked, 1):

print(f&quot; {rank}위: 《{DOCS[doc_id]['title']}》 점수={score:.4f}&quot;)

  
  

def main():

corpus = [doc[&quot;text&quot;] for doc in DOCS]

  

# 검색기 초기화 (오프라인 인덱싱 단계)

bm25 = SimpleBM25(corpus)

dense = DenseRetriever(corpus)

  

print(&quot;=&quot; * 60)

print(&quot;요리 레시피 검색 비교: 희소검색 vs 밀집검색 vs 앙상블&quot;)

print(&quot;=&quot; * 60)

  

# ── 쿼리 1: 키워드가 정확히 일치 &amp;rarr; BM25(희소검색)가 유리 ──

query1 = &quot;파스타 올리브오일 요리 추천&quot;

print(f&quot;\n▶ 쿼리 1 (키워드 정확 일치 유리): \&quot;{query1}\&quot;&quot;)

b1, d1 = bm25.score(query1), dense.score(query1)

print_results(&quot;희소검색 (BM25)&quot;, b1)

print_results(&quot;밀집검색 (코사인)&quot;, d1)

print_results(&quot;앙상블 (0.5 : 0.5)&quot;, ensemble_score(b1, d1))

  

# ── 쿼리 2: 키워드 불일치, 의미 유사 &amp;rarr; 밀집검색이 유리 ──

query2 = &quot;이탈리아 전통 오븐 요리&quot;

print(f&quot;\n▶ 쿼리 2 (의미 기반 검색 유리): \&quot;{query2}\&quot;&quot;)

b2, d2 = bm25.score(query2), dense.score(query2)

print_results(&quot;희소검색 (BM25)&quot;, b2)

print_results(&quot;밀집검색 (코사인)&quot;, d2)

print_results(&quot;앙상블 (0.5 : 0.5)&quot;, ensemble_score(b2, d2))

  

print(&quot;\n&quot; + &quot;=&quot; * 60)

print(&quot;앙상블은 한쪽이 놓친 결과를 다른 쪽이 보완해 균형을 맞춘다.&quot;)

print(&quot;=&quot; * 60)

  
  

if __name__ == &quot;__main__&quot;:

main()

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 방식을 직접 돌려보면 '정답으로 정해진 하나의 검색법은 없다'는 게 분명해진다. 검색 대상이 고유명사&amp;middot;전문 용어 위주라면 키워드 쪽으로, 표현이 제각각인 일반 질문이라면 의미 쪽으로 무게를 옮기는 것 &amp;mdash; 결국 검색 알고리즘 선택도 앞선 기법들과 똑같이, 내 데이터와 질문의 성격을 먼저 보고 저울질하는 일이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/RAG</category>
      <category>AI</category>
      <category>Rag</category>
      <category>고도화</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/199</guid>
      <comments>https://kimback03.tistory.com/199#entry199comment</comments>
      <pubDate>Sat, 27 Jun 2026 16:47:02 +0900</pubDate>
    </item>
    <item>
      <title>RAG 성능 고도화 &amp;mdash; HyDE, 가상의 답을 지어내 검색하기</title>
      <link>https://kimback03.tistory.com/198</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;RAG 성능 고도화 &amp;mdash; HyDE, 가상의 답을 지어내 검색하기&quot;&gt;RAG 성능 고도화 &amp;mdash; HyDE, 가상의 답을 지어내 검색하기&lt;/h1&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 RAG 검색 정확도를 끌어올리는 'HyDE(가상 문서 임베딩)'를 탐구하며 정리한 기록이다. 책과 Gemini의 도움을 빌려 궁금증을 해소해가며 쌓았다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;질문을 그대로 검색하지 말고, '답변'을 먼저 지어내서 검색한다&quot; data-ke-size=&quot;size26&quot;&gt;질문을 그대로 검색하지 말고, '답변'을 먼저 지어내서 검색한다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞 글에서 다중 질의로 질문을 여러 갈래로 늘려 검색망을 넓히는 방법을 정리했는데, 같은 '질의 변형' 계열인데도 발상이 완전히 다른 기법이 하나 더 있다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;HyDE(Hypothetical Document Embeddings)&lt;/b&gt;, 우리말로 옮기면 '가상 문서 임베딩'이다. 질문을 더 잘 다듬어서 검색하는 게 아니라, 아예 그 질문에 대한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;가상의 답변을 LLM에게 먼저 지어내게 한 다음, 그 답변으로 DB를 서칭하는&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;방식이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음 이 아이디어를 봤을 때는 좀 이상하게 들렸다. 답을 모르니까 검색하는 건데, 답을 먼저 만든다니 앞뒤가 안 맞는 것 같았다. 그런데 곱씹어보면 꽤 영리한 우회다. 핵심은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;질문과 답변은 생긴 게 다르다&lt;/b&gt;는 점에 있다. 우리가 찾으려는 대상 문서는 결국 '답변에 가까운 글'인데, 정작 검색에 넣는 사용자의 질문은 형태가 답변과 딴판이다. 짧고 막연한 의문문 한 줄로 길고 구체적인 설명문 더미를 찾으려니 임베딩 공간에서 거리가 멀 수밖에 없다. HyDE는 이 어긋남을 정면으로 공략한다. 질문보다 답변이 대상 문서와 훨씬 닮았으니, 차라리 그럴듯한 가짜 답변을 만들어 그걸로 검색하면 진짜 정답 문서와 더 가까이 붙는다는 직관이다. 이 발상은 Luyu Gao 등이 쓴 논문&lt;span&gt;&amp;nbsp;&lt;/span&gt;Precise Zero-Shot Dense Retrieval without Relevance Labels(2022년 12월 arXiv, ACL 2023 발표)에서 제안됐다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;전체 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;flowchart TD

A[&quot;사용자 질문 입력&quot;] --&amp;gt; B[&quot;LLM이 가상의 답변&amp;lt;br/&amp;gt;(Hypothetical Document) 생성&quot;]

B --&amp;gt; C[&quot;가상 답변 임베딩&quot;]

C --&amp;gt; D[&quot;벡터 DB에서 유사 문서 검색&amp;lt;br/&amp;gt;(질문이 아닌 가상 답변으로 검색)&quot;]

D --&amp;gt; E[&quot;검색된 문서 + 원래 질문을&amp;lt;br/&amp;gt;LLM에 전달&quot;]

E --&amp;gt; F[&quot;최종 답변 생성&quot;]

&lt;/code&gt;&lt;/pre&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;주식 질문 하나로 따라가 보는 HyDE&quot; data-ke-size=&quot;size26&quot;&gt;주식 질문 하나로 따라가 보는 HyDE&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;말로만 들으면 감이 안 오니 원문에서 든 예시를 그대로 따라가 보자. 사용자가 &quot;주식시장 변동성이 높을 때 투자 전략은 무엇인가요?&quot;라고 물었다고 하자. 이걸 단순 키워드 검색에 그대로 넣으면, 시스템이 건질 수 있는 단서는 '주식시장', '변동성', '투자전략' 정도가 전부다. 너무 앙상해서 정작 관련성 낮은 문서가 딸려 올 여지가 크다. 질문 문장에 들어 있는 키워드 자체가 빈약한 게 문제다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기서 HyDE는 검색에 들어가기 전에 LLM에게 이 질문의 가상 답변을 먼저 받아낸다. 가령 &quot;주식시장의 변동성이 높을 때는 분산 투자, 달러 코스트 애버리징, 안전 자산 비중 확대 등의 전략을 세울 수 있다&quot; 정도의 답변이 나온다. 이게 사실인지 아닌지는 중요하지 않다. 어차피 사용자에게 보여줄 최종 답이 아니라 검색용 미끼니까. 중요한 건 이 가상 답변에 '분산 투자', '달러 코스트 애버리징', '안전 자산', '비중 확대'처럼 원래 질문에는 없던 풍부한 키워드가 잔뜩 박혀 있다는 점이다. 질문 한 줄을 던졌을 때와 이 가상 답변을 던졌을 때, 검색에 쓰이는 단서의 밀도가 확 차이 난다. 이 풍부해진 단서로 DB를 뒤지면 훨씬 정확하고 관련성 높은 문서를 찾아낼 가능성이 크게 올라간다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;쉽고 빠른 대신, 공짜는 아니다&quot; data-ke-size=&quot;size26&quot;&gt;쉽고 빠른 대신, 공짜는 아니다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;HyDE의 가장 큰 매력은 적용이 쉽고 빠르다는 데 있다. 기존 검색 파이프라인 앞에 '가상 답변 생성' 단계 하나만 끼워 넣으면 끝이라, 적은 손으로 검색 품질을 끌어올릴 수 있다. 앞 글의 다중 질의가 그랬듯, 이 계열 기법들은 대체로 투입 대비 효과가 좋아서 먼저 손대볼 만하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다만 공짜는 아니다. 검색을 한 번 돌리자고 LLM을 한 번 더 호출해 가상 답변을 만들어야 하니, 그만큼 비용이 추가되고 응답 시간도 늘어난다. 결국 다중 질의 때와 똑같은 저울질이다. 가상 답변을 만드는 리소스와 지연을 내주고, 그 대가로 검색 적중률을 사는 거래다. 질문과 답변의 생김새가 크게 어긋나는 도메인일수록 이 거래가 남는 장사라고 판단했다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;실습 예제&quot; data-ke-size=&quot;size26&quot;&gt;실습 예제&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드는 개념 자체가 한눈에 들어오도록 라이브러리 없이 순수 파이썬으로, 도메인도 주식 대신 '건강 상담'으로 바꿔 새로 짰다. 질문을 그대로 검색했을 때와 가상 답변으로 검색했을 때를 나란히 찍어, 차이를 눈으로 확인하게 했다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;&quot;&quot;&quot;

HyDE (Hypothetical Document Embeddings) 실습 예제

도메인: 건강/증상 상담

핵심 교육 포인트: 같은 질문을 (A) 그대로 검색 vs (B) 가상답변으로 검색 비교

&quot;&quot;&quot;

  

import math

import re

from collections import Counter

  

# ─────────────────────────────────────────────

# 1. 간이 문서 DB (건강&amp;middot;증상 관련 지식 조각)

# ─────────────────────────────────────────────

DOCUMENTS = [

{&quot;id&quot;: 1, &quot;content&quot;: &quot;충분한 수분 섭취는 두통 완화에 효과적이다. 성인은 하루 약 2리터의 물을 마셔야 한다.&quot;},

{&quot;id&quot;: 2, &quot;content&quot;: &quot;긴장성 두통은 목&amp;middot;어깨 근육의 과도한 수축으로 발생한다. 스트레칭과 온찜질이 도움이 된다.&quot;},

{&quot;id&quot;: 3, &quot;content&quot;: &quot;편두통은 박동성 통증, 빛&amp;middot;소리 과민, 구역감을 동반하는 경우가 많다. 어두운 방에서 안정을 취하면 증상이 완화된다.&quot;},

{&quot;id&quot;: 4, &quot;content&quot;: &quot;마그네슘 결핍은 두통과 근육 경련을 유발할 수 있다. 견과류, 통곡물, 녹색 채소를 통해 보충할 수 있다.&quot;},

{&quot;id&quot;: 5, &quot;content&quot;: &quot;과음 후 두통(숙취 두통)은 알코올의 이뇨 작용으로 인한 탈수가 주요 원인이다. 전해질 음료 섭취가 권장된다.&quot;},

{&quot;id&quot;: 6, &quot;content&quot;: &quot;만성 두통이 주 3회 이상 지속되면 신경과 전문의 진료가 필요하다. CT나 MRI 검사로 기질적 원인을 배제한다.&quot;},

{&quot;id&quot;: 7, &quot;content&quot;: &quot;카페인 금단 증상으로 두통이 나타날 수 있다. 평소 커피를 즐기다가 갑자기 중단하면 24~48시간 내에 두통이 발생한다.&quot;},

{&quot;id&quot;: 8, &quot;content&quot;: &quot;혈압이 갑자기 상승하면 후두부에 두통이 발생할 수 있다. 심한 두통과 함께 시야 이상&amp;middot;구토가 있으면 즉시 응급실을 방문해야 한다.&quot;},

{&quot;id&quot;: 9, &quot;content&quot;: &quot;수면 부족은 두통의 흔한 원인이다. 규칙적인 수면 패턴을 유지하고 하루 7~9시간을 자는 것이 권장된다.&quot;},

{&quot;id&quot;: 10, &quot;content&quot;: &quot;목 디스크(경추 추간판 탈출증)는 후두부와 어깨&amp;middot;팔로 방사되는 통증을 유발할 수 있다. X-ray 또는 MRI로 진단한다.&quot;},

]

  
  

# ─────────────────────────────────────────────

# 2. 간이 TF-IDF 유사도 계산 (외부 라이브러리 없음)

# 실제 RAG에서는 임베딩 벡터의 코사인 유사도를 쓴다.

# ─────────────────────────────────────────────

def tokenize(text: str) -&amp;gt; list[str]:

&quot;&quot;&quot;한국어&amp;middot;영문 단어 단위 토큰화 (공백&amp;middot;구두점 기준)&quot;&quot;&quot;

return re.findall(r&quot;[가-힣a-zA-Z0-9]+&quot;, text.lower())

  
  

def build_idf(docs: list[dict]) -&amp;gt; dict[str, float]:

&quot;&quot;&quot;역문서빈도(IDF) 계산&quot;&quot;&quot;

n = len(docs)

df: Counter = Counter()

for doc in docs:

df.update(set(tokenize(doc[&quot;content&quot;])))

return {term: math.log((n + 1) / (count + 1)) + 1 for term, count in df.items()}

  
  

def tf_idf_vector(text: str, idf: dict) -&amp;gt; dict[str, float]:

&quot;&quot;&quot;텍스트의 TF-IDF 벡터 반환&quot;&quot;&quot;

tokens = tokenize(text)

if not tokens:

return {}

tf = Counter(tokens)

total = len(tokens)

return {term: (count / total) * idf.get(term, 0) for term, count in tf.items()}

  
  

def cosine_similarity(vec_a: dict, vec_b: dict) -&amp;gt; float:

&quot;&quot;&quot;두 TF-IDF 벡터의 코사인 유사도&quot;&quot;&quot;

common = set(vec_a) &amp;amp; set(vec_b)

if not common:

return 0.0

dot = sum(vec_a[t] * vec_b[t] for t in common)

norm_a = math.sqrt(sum(v ** 2 for v in vec_a.values()))

norm_b = math.sqrt(sum(v ** 2 for v in vec_b.values()))

if norm_a == 0 or norm_b == 0:

return 0.0

return dot / (norm_a * norm_b)

  
  

def search(query: str, idf: dict, top_k: int = 3) -&amp;gt; list[dict]:

&quot;&quot;&quot;쿼리와 가장 유사한 문서 top_k개 반환&quot;&quot;&quot;

q_vec = tf_idf_vector(query, idf)

scored = []

for doc in DOCUMENTS:

d_vec = tf_idf_vector(doc[&quot;content&quot;], idf)

scored.append({&quot;id&quot;: doc[&quot;id&quot;], &quot;score&quot;: cosine_similarity(q_vec, d_vec), &quot;content&quot;: doc[&quot;content&quot;]})

scored.sort(key=lambda x: x[&quot;score&quot;], reverse=True)

return scored[:top_k]

  
  

# ─────────────────────────────────────────────

# 3. HyDE 핵심: 가상답변 생성기

# [실제로는 LLM(ChatGPT&amp;middot;Claude 등)이 생성]

# 여기서는 규칙 기반 템플릿으로 흉내 낸다.

# ─────────────────────────────────────────────

HYPOTHETICAL_TEMPLATES = {

&quot;두통&quot;: (

&quot;두통이 자주 발생할 때는 먼저 수분 섭취가 충분한지 확인해야 한다. &quot;

&quot;긴장성 두통이라면 목&amp;middot;어깨 스트레칭과 온찜질이 도움이 되며, &quot;

&quot;편두통은 어두운 방에서 안정을 취하는 것이 권장된다. &quot;

&quot;마그네슘 결핍이 원인일 수 있으므로 견과류&amp;middot;녹색 채소 섭취도 고려한다. &quot;

&quot;수면 부족이나 카페인 금단도 흔한 원인이며, 만성화되면 신경과 진료가 필요하다.&quot;

),

}

  
  

def generate_hypothetical_answer(question: str) -&amp;gt; str:

&quot;&quot;&quot;

[실제로는 LLM이 생성]

질문 키워드를 기반으로 가상의 전문가 답변을 생성한다.

실제 서비스에서는 ChatGPT&amp;middot;Claude 등 LLM API 호출로 대체한다.

&quot;&quot;&quot;

for keyword, template in HYPOTHETICAL_TEMPLATES.items():

if keyword in question:

return template

return question # 매칭 실패 시 질문을 그대로 반환 (폴백)

  
  

# ─────────────────────────────────────────────

# 4. (A) vs (B) 비교 실행

# ─────────────────────────────────────────────

def print_results(label: str, query_used: str, results: list[dict]) -&amp;gt; None:

print(f&quot;\n{'='*60}&quot;)

print(f&quot; {label}&quot;)

shown = query_used if len(query_used) &amp;lt;= 60 else query_used[:60] + &quot;...&quot;

print(f&quot; 검색에 사용된 텍스트: \&quot;{shown}\&quot;&quot;)

print(f&quot;{'='*60}&quot;)

for rank, r in enumerate(results, start=1):

print(f&quot; [{rank}위 | 유사도={r['score']:.4f}] {r['content'][:50]}...&quot;)

  
  

def main():

idf = build_idf(DOCUMENTS)

user_question = &quot;두통이 자꾸 생겨요&quot;

  

print(&quot;\n&quot; + &quot;★&quot; * 60)

print(&quot; HyDE 비교 데모: 건강/증상 상담 도메인&quot;)

print(&quot; 사용자 질문:&quot;, user_question)

print(&quot;★&quot; * 60)

  

# (A) 질문 그대로 검색

results_a = search(user_question, idf, top_k=3)

print_results(&quot;(A) 질문 원문으로 직접 검색&quot;, user_question, results_a)

  

# (B) 가상답변 생성 후 그 답변으로 검색

hypothetical_answer = generate_hypothetical_answer(user_question)

results_b = search(hypothetical_answer, idf, top_k=3)

print_results(&quot;(B) HyDE &amp;mdash; 가상답변을 검색 쿼리로 사용&quot;, hypothetical_answer, results_b)

  

# 비교 요약

print(&quot;\n&quot; + &quot;-&quot; * 60)

print(&quot; [비교 요약]&quot;)

print(f&quot; (A) 검색된 문서 ID: {[r['id'] for r in results_a]}&quot;)

print(f&quot; (B) 검색된 문서 ID: {[r['id'] for r in results_b]}&quot;)

avg_a = sum(r[&quot;score&quot;] for r in results_a) / len(results_a)

avg_b = sum(r[&quot;score&quot;] for r in results_b) / len(results_b)

print(f&quot; (A) 평균 유사도: {avg_a:.4f}&quot;)

print(f&quot; (B) 평균 유사도: {avg_b:.4f}&quot;)

if avg_b &amp;gt; avg_a:

print(&quot; &amp;rarr; HyDE(B)가 더 높은 평균 유사도로 관련 문서를 더 잘 찾았습니다.&quot;)

else:

print(&quot; &amp;rarr; 결과 동일 또는 (A) 우위. 가상답변 품질 개선이 필요합니다.&quot;)

print(&quot;-&quot; * 60)

  
  

if __name__ == &quot;__main__&quot;:

main()

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 (A)와 (B)를 나란히 돌려보면 HyDE의 값어치가 '질문과 답변 사이의 거리'를 좁히는 데서 나온다는 게 눈에 들어온다. 앞 글의 다중 질의가 질문을 여러 개로 늘리는 길이었다면, HyDE는 질문을 답의 모습으로 바꿔치기하는 길이다. 방향은 달라도 둘 다 '검색 앞단에서 LLM에게 일을 한 번 더 시켜, 검색을 쉽게 만든다'는 같은 줄기에 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RAG 마스터 레시피 - 브라이스 유 , 조경아 , 박수진 , 김재웅&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216240484&quot;&gt;https://product.kyobobook.co.kr/detail/S000216240484&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테디노트의 랭체인을 활용한 RAG 비법노트&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216574552&quot;&gt;https://product.kyobobook.co.kr/detail/S000216574552&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI Engineering/RAG</category>
      <category>AI</category>
      <category>Rag</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/198</guid>
      <comments>https://kimback03.tistory.com/198#entry198comment</comments>
      <pubDate>Mon, 22 Jun 2026 16:04:54 +0900</pubDate>
    </item>
    <item>
      <title>RAG 성능 고도화 &amp;mdash; 질의 변형과 다중 질의 생성</title>
      <link>https://kimback03.tistory.com/197</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;RAG 성능 고도화 &amp;mdash; 질의 변형과 다중 질의 생성&quot;&gt;RAG 성능 고도화 &amp;mdash; 질의 변형과 다중 질의 생성&lt;/h1&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 RAG 검색 단의 정확도를 끌어올리는 '질의 변형'을 직접 적용해보며 정리한 기록이다. 청킹으로 문서를 어떻게 자를지 고민한 데 이어, 이번엔 검색에 들어가는 '질문' 그 자체를 손보는 이야기다. 책과 Gemini의 도움을 빌려 궁금증을 해소해가며 쌓았다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;사용자의 질문을 그대로 검색하면, 왜 자꾸 헛소리만 늘어놓을까?&quot; data-ke-size=&quot;size26&quot;&gt;사용자의 질문을 그대로 검색하면, 왜 자꾸 헛소리만 늘어놓을까?&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RAG를 어느 정도 굴려보고 나서 가장 먼저 부딪힌 벽은, 사람들이 생각보다 질문을 엉성하게 한다는 사실이었다. 검색 단에서 청킹이고 임베딩이고 다 잘 깔아놨는데도 엉뚱한 문서가 올라오길래 한참을 들여다봤더니, 문제의 절반은 시스템이 아니라 입력으로 들어온 질문 그 자체에 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;벡터 검색은 단어가 똑같으냐가 아니라 뜻이 비슷하냐를 따진다고는 하지만, 그 &quot;비슷함&quot;에도 한계가 있다. 사용자가 &quot;컴퓨터 고치는 법&quot;이라고 물었는데 정작 문서에는 &quot;PC 수리 가이드&quot;라고 적혀 있다면, 임베딩 공간에서 두 표현의 거리가 멀어 검색이 그냥 실패해버린다. 같은 걸 묻고 있는데도 단어 선택 하나가 어긋나서 정답 문서를 놓치는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 나온 발상이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;질의 변형(Query Transformation)&lt;/b&gt;이다. 사용자가 던진 원래 질문을 그대로 검색에 쓰지 않고, 더 잘 걸리는 검색 쿼리로 한 번 다시 써서 넣는 과정이다. 쉽게 말하면 면접관 앞에서 횡설수설하는 지원자의 말을, 옆에 있던 베테랑이 &quot;아, 이분이 지금 묻고 싶은 건 이거예요&quot;라고 정리해주는 셈이다. 사용자가 대충 물어도 시스템이 알아듣게 만드는 장치다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 질문을 다시 쓰는 주체는 내가 아니라 LLM이다. 원래 질문을 분석하고 재구성하는 일을 LLM에게 맡긴다. 이 기법이 매력적인 이유는 적용 난이도에 비해 효과가 크다는 데 있다. 파이프라인을 갈아엎지 않고도 검색 앞단에 변형 단계 하나만 끼워 넣으면 되는데, 그 대가로 훨씬 풍부하고 실용적인 정보를 끌어올 수 있다. 손이 적게 가면서 성능이 올라가는, 흔치 않은 개선책이라 먼저 손대볼 만하다고 봤다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;질문 하나로 부족하면, 여러 개로 늘려서 검색한다&quot; data-ke-size=&quot;size26&quot;&gt;질문 하나로 부족하면, 여러 개로 늘려서 검색한다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;질의 변형 중에서도 가장 먼저 시도해본 게&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;다중 질의 생성(Multi-Query Generation)&lt;/b&gt;이다. 이름 그대로, 하나의 질문을 LLM에게 줘서 표현이 조금씩 다른 여러 개의 질문으로 불려놓는 방법이다. 질문 하나로 검색하면 그 한 번의 표현에 운명을 거는 셈인데, 여러 표현으로 동시에 던지면 어느 하나는 정답 문서에 걸릴 확률이 훨씬 높아진다. 그물을 한 번만 던지는 대신, 각도를 달리해 여러 번 던지는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;흐름은 단순하다. 원래 질문을 LLM이 서로 다른 표현의 질문 여러 개로 확장하고, 그 질문들을 병렬로 각각 문서 검색에 넣는다. 그렇게 나온 결과들을 한데 모으면 같은 문서가 여기저기서 중복으로 딸려 오기 마련이라, 중복을 제거한 뒤 하나로 통합해 최종 결과로 넘긴다. 단일 쿼리였다면 표현이 어긋나 놓쳤을 문서까지 건져 올리니, 그만큼 더 풍부한 맥락을 답변 생성에 쥐여줄 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;말로만 보면 감이 잘 안 오니 실제 예시로 보자. 사용자가 &quot;주식투자 처음 시작하려면 어떻게 해야 하죠?&quot;라고 물었다고 하자. 딱 봐도 막연하고 검색하기 까다로운 질문이다. 이걸 그대로 벡터 DB에 넣으면 어디에 초점을 맞춰야 할지 애매하다. 다중 질의 생성은 이 한 문장을 LLM에게 넘겨, 초점이 다른 세 갈래 질문으로 다시 써낸다. &quot;초보 투자자를 위한 주식 투자 기초지식은 무엇인가&quot;, &quot;주식 시장 분석을 위한 기본적인 재무제표 읽는 방법은&quot;, &quot;주식투자 시작 전 필요한 자금 관리 전략은&quot;처럼 말이다. 막연했던 한 질문이 기초 지식&amp;middot;재무제표&amp;middot;자금 관리라는 구체적인 세 방향으로 쪼개지고, 각각이 따로 검색에 들어간다. 단일 쿼리 하나로는 분명 놓쳤을 영역까지 골고루 훑게 되는 것이다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #000000; text-align: start;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;graph TD
A[&quot;사용자의 모호한 질문 1개&quot;] --&amp;gt; B[&quot;LLM: 질문 재생성&amp;lt;br/&amp;gt;(3~5개 다른 표현으로 확장)&quot;]
B --&amp;gt; C[&quot;질문 1&quot;]
B --&amp;gt; D[&quot;질문 2&quot;]
B --&amp;gt; E[&quot;질문 3&quot;]

C --&amp;gt; F[&quot;벡터 DB 검색 1&quot;]
D --&amp;gt; G[&quot;벡터 DB 검색 2&quot;]
E --&amp;gt; H[&quot;벡터 DB 검색 3&quot;]

F --&amp;gt; I[&quot;검색 결과 통합&amp;lt;br/&amp;gt;(합집합)&quot;]
G --&amp;gt; I
H --&amp;gt; I

I --&amp;gt; J[&quot;중복 제거&quot;]
J --&amp;gt; K[&quot;풍부해진 문맥(Context)&amp;lt;br/&amp;gt;LLM에 전달&quot;]
K --&amp;gt; L[&quot;최종 답변 생성&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 흐름을 머릿속으로만 그리지 말고 작은 코드로 직접 돌려보는 편이 이해가 빠르다. LangChain에는 이 다중 질의 생성을 통째로 구현해둔&lt;span&gt;&amp;nbsp;&lt;/span&gt;MultiQueryRetriever가 있어서, 검색기와 LLM만 물려주면 질문 확장부터 결과 통합까지 알아서 처리해준다. 다만 그 안에서 무슨 일이 벌어지는지 감을 잡으려면, 라이브러리를 걷어내고 핵심 흐름만 직접 짜보는 게 빠르다. 외부 API 없이 '여행지 추천' 검색을 예로, 막연한 질문 하나가 여러 질문으로 늘어나 검색되고 다시 하나로 합쳐지는 과정을 그대로 옮겨봤다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;# 다중 질의(Multi-Query) 검색 예제 &amp;mdash; 여행지 추천 도메인

# 외부 라이브러리 없이 파이썬 표준 라이브러리만 사용

  
  

# ── 1. 여행 문서 모음 ──────────────────────────────────────────────────────────

# 실제 RAG에서는 벡터 DB에 저장된 청크들이 이 역할을 한다.

TRAVEL_DOCS = [

{

&quot;id&quot;: &quot;doc_001&quot;,

&quot;title&quot;: &quot;제주도 자연 여행&quot;,

&quot;content&quot;: &quot;제주도는 한라산과 성산일출봉 등 유네스코 세계자연유산을 품고 있다. &quot;

&quot;오름 트레킹과 해안 올레길 걷기가 대표 액티비티이며, 봄에는 유채꽃 명소로 유명하다.&quot;,

},

{

&quot;id&quot;: &quot;doc_002&quot;,

&quot;title&quot;: &quot;제주도 맛집과 카페&quot;,

&quot;content&quot;: &quot;제주도는 흑돼지 바베큐, 갈치조림, 해산물 회가 유명하다. &quot;

&quot;애월 해안도로를 따라 감성 카페들이 즐비하며 오션뷰 커피를 즐길 수 있다.&quot;,

},

{

&quot;id&quot;: &quot;doc_003&quot;,

&quot;title&quot;: &quot;교토 전통 문화 여행&quot;,

&quot;content&quot;: &quot;교토는 금각사, 아라시야마 대나무 숲, 후시미이나리 신사 등 고즈넉한 일본 전통 명소가 많다. &quot;

&quot;기온 마쓰리 등 계절 축제와 다도 체험, 기모노 투어도 인기다.&quot;,

},

{

&quot;id&quot;: &quot;doc_004&quot;,

&quot;title&quot;: &quot;방콕 도심 관광&quot;,

&quot;content&quot;: &quot;방콕은 왓 프라깨우 왕궁사원, 왓 아룬, 수상시장이 유명하다. &quot;

&quot;야시장과 쇼핑몰, 길거리 음식 투어는 자유여행자들에게 특히 인기 있다.&quot;,

},

{

&quot;id&quot;: &quot;doc_005&quot;,

&quot;title&quot;: &quot;유럽 배낭여행 코스&quot;,

&quot;content&quot;: &quot;파리 에펠탑과 루브르 박물관, 로마 콜로세움과 바티칸, 바르셀로나 사그라다 파밀리아는 &quot;

&quot;유럽 배낭여행의 3대 필수 코스다. 유레일 패스를 활용하면 도시 간 이동이 편리하다.&quot;,

},

{

&quot;id&quot;: &quot;doc_006&quot;,

&quot;title&quot;: &quot;강원도 힐링 여행&quot;,

&quot;content&quot;: &quot;강원도 속초와 강릉은 설악산 등반과 경포대 해수욕장으로 유명하다. &quot;

&quot;겨울에는 평창 스키 리조트가, 가을에는 단풍 드라이브 코스가 인기다.&quot;,

},

{

&quot;id&quot;: &quot;doc_007&quot;,

&quot;title&quot;: &quot;발리 해변 휴양&quot;,

&quot;content&quot;: &quot;발리는 꾸따 비치와 우붓 라이스 테라스가 대표 명소다. &quot;

&quot;서핑, 스노클링, 스쿠버 다이빙 등 해양 액티비티가 풍부하고 &quot;

&quot;힌두 사원과 일몰 명소인 타나롯 사원도 빼놓을 수 없다.&quot;,

},

{

&quot;id&quot;: &quot;doc_008&quot;,

&quot;title&quot;: &quot;서울 도심 여행&quot;,

&quot;content&quot;: &quot;서울은 경복궁, 북촌 한옥마을, 인사동 전통 거리, 남산타워가 주요 관광지다. &quot;

&quot;홍대, 이태원, 성수동은 젊은 여행자들에게 인기 있는 핫플레이스 지역이다.&quot;,

},

]

  
  

# ── 2. 단순 키워드 기반 유사도 계산 ──────────────────────────────────────────

# 실제 RAG에서는 임베딩 벡터의 코사인 유사도로 계산한다.

# 여기서는 쿼리 단어가 문서에 '부분 문자열'로 들어있는지 세어 흉내 낸다.

# (한국어는 '맛집'과 '맛집과'처럼 조사가 붙으므로 단순 일치보다 부분 포함이 잘 맞는다.)

def tokenize(text: str) -&amp;gt; list[str]:

&quot;&quot;&quot;텍스트를 소문자 공백 분리 토큰 리스트로 변환한다.&quot;&quot;&quot;

return text.lower().split()

  
  

def keyword_match_score(query: str, doc_text: str) -&amp;gt; float:

&quot;&quot;&quot;

쿼리 단어 중 문서 본문에 포함된 단어의 비율을 유사도 점수로 반환한다.

실제로는 임베딩 벡터의 코사인 유사도를 사용한다.

&quot;&quot;&quot;

query_tokens = set(tokenize(query))

if not query_tokens:

return 0.0

doc_text = doc_text.lower()

hits = sum(1 for t in query_tokens if t in doc_text) # 부분 문자열 포함 검사

return hits / len(query_tokens)

  
  

def search_docs(query: str, top_k: int = 3, threshold: float = 0.1) -&amp;gt; list[dict]:

&quot;&quot;&quot;확장 질문 하나로 문서를 검색해 상위 결과를 반환한다.&quot;&quot;&quot;

scores = []

for doc in TRAVEL_DOCS:

score = keyword_match_score(query, doc[&quot;title&quot;] + &quot; &quot; + doc[&quot;content&quot;])

if score &amp;gt;= threshold:

scores.append((score, doc))

  

scores.sort(key=lambda x: x[0], reverse=True)

return [doc for _, doc in scores[:top_k]]

  
  

# ── 3. LLM 역할: 질문 확장 함수 ──────────────────────────────────────────────

# 실제로는 LLM이 원래 질문을 받아 다른 관점의 질문들을 생성한다.

# 여기서는 간단한 규칙 사전으로 흉내를 낸다.

EXPANSION_RULES = {

&quot;자연&quot;: [&quot;오름 트레킹 등산 명소&quot;, &quot;유네스코 세계자연유산 국내여행&quot;, &quot;산 바다 자연 경관 힐링 여행&quot;],

&quot;문화&quot;: [&quot;전통 사찰 역사 유적 투어&quot;, &quot;박물관 유네스코 문화유산 관광&quot;, &quot;기모노 한복 전통 체험 여행&quot;],

&quot;음식&quot;: [&quot;맛집 길거리 음식 야시장 투어&quot;, &quot;해산물 현지 특산물 미식 여행&quot;, &quot;카페 디저트 감성 식도락 여행&quot;],

&quot;해변&quot;: [&quot;비치 해수욕장 서핑 해양 액티비티&quot;, &quot;스노클링 스쿠버 열대 바다 여행&quot;, &quot;오션뷰 일몰 리조트 휴양&quot;],

&quot;유럽&quot;: [&quot;파리 로마 바르셀로나 배낭여행&quot;, &quot;에펠탑 콜로세움 유럽 명소 관광&quot;, &quot;유레일 패스 유럽 도시 여행&quot;],

&quot;일본&quot;: [&quot;교토 오사카 도쿄 일본 여행&quot;, &quot;벚꽃 단풍 일본 계절 여행&quot;, &quot;신사 사원 일본 전통 문화 투어&quot;],

}

DEFAULT_EXPANSIONS = [

&quot;국내 여행 추천 명소 관광&quot;,

&quot;해외 여행 핫플레이스 코스&quot;,

&quot;자유 여행 배낭여행 추천 루트&quot;,

]

  
  

def expand_query_with_rules(original_query: str, n: int = 3) -&amp;gt; list[str]:

&quot;&quot;&quot;

규칙 사전으로 원래 질문을 n개의 확장 질문으로 변환한다.

매칭된 여러 카테고리에서 번갈아 하나씩 뽑아 '서로 다른 각도'를 확보한다.

실제로는 LLM이 이 작업을 수행하며, 문맥과 의도를 더 정교하게 반영한다.

&quot;&quot;&quot;

matched = [queries for kw, queries in EXPANSION_RULES.items() if kw in original_query]

  

expanded: list[str] = []

if matched:

# 라운드로빈: 카테고리1[0], 카테고리2[0], 카테고리1[1] ... 순으로 다양하게 섞는다

depth = max(len(q) for q in matched)

for i in range(depth):

for queries in matched:

if i &amp;lt; len(queries):

expanded.append(queries[i])

  

# 매칭이 없거나 부족하면 기본 확장으로 보충

if len(expanded) &amp;lt; n:

expanded.extend(DEFAULT_EXPANSIONS)

  

return expanded[:n]

  
  

# ── 4. 다중 질의 검색 실행 ────────────────────────────────────────────────────

def multi_query_search(original_query: str, top_k_per_query: int = 3) -&amp;gt; dict:

&quot;&quot;&quot;

다중 질의(Multi-Query) 검색의 핵심 흐름:

1) 원래 질문을 여러 확장 질문으로 변환

2) 각 확장 질문으로 개별 검색 수행

3) 결과 합집합(union)에서 중복 제거

&quot;&quot;&quot;

# 질문 확장 &amp;mdash; 실제로는 LLM이 생성한다

expanded_queries = expand_query_with_rules(original_query)

  

# 각 확장 질문별 검색 결과 수집

per_query_results: dict[str, list[dict]] = {}

for query in expanded_queries:

per_query_results[query] = search_docs(query, top_k=top_k_per_query)

  

# 결과 합집합: doc id 기준으로 중복 제거

seen_ids: set[str] = set()

final_docs: list[dict] = []

for query, docs in per_query_results.items():

for doc in docs:

if doc[&quot;id&quot;] not in seen_ids:

seen_ids.add(doc[&quot;id&quot;])

final_docs.append(doc)

  

return {

&quot;original_query&quot;: original_query,

&quot;expanded_queries&quot;: expanded_queries,

&quot;per_query_results&quot;: per_query_results,

&quot;final_docs&quot;: final_docs,

}

  
  

# ── 5. 결과 출력 ──────────────────────────────────────────────────────────────

def print_results(result: dict) -&amp;gt; None:

print(&quot;=&quot; * 65)

print(f&quot;[원래 질문] {result['original_query']}&quot;)

print(&quot;=&quot; * 65)

  

print(&quot;\n▶ 확장된 질문 목록 (실제로는 LLM이 생성한다)&quot;)

for i, q in enumerate(result[&quot;expanded_queries&quot;], 1):

print(f&quot; Q{i}. {q}&quot;)

  

print(&quot;\n&quot; + &quot;-&quot; * 65)

print(&quot;▶ 확장 질문별 검색 결과&quot;)

for q, docs in result[&quot;per_query_results&quot;].items():

print(f&quot;\n [질문] {q}&quot;)

if docs:

for doc in docs:

print(f&quot; &amp;rarr; {doc['title']} (id: {doc['id']})&quot;)

else:

print(&quot; &amp;rarr; 검색 결과 없음&quot;)

  

print(&quot;\n&quot; + &quot;-&quot; * 65)

print(f&quot;▶ 중복 제거 후 최종 결과 ({len(result['final_docs'])}건)&quot;)

for i, doc in enumerate(result[&quot;final_docs&quot;], 1):

print(f&quot;\n [{i}] {doc['title']}&quot;)

preview = doc[&quot;content&quot;][:60] + (&quot;...&quot; if len(doc[&quot;content&quot;]) &amp;gt; 60 else &quot;&quot;)

print(f&quot; {preview}&quot;)

print(&quot;=&quot; * 65)

  
  

# ── 6. 데모 실행 ──────────────────────────────────────────────────────────────

if __name__ == &quot;__main__&quot;:

# 사용자의 모호한 원래 질문 1개

user_question = &quot;자연도 보고 음식도 즐길 수 있는 여행지 추천해줘&quot;

  

result = multi_query_search(user_question, top_k_per_query=3)

print_results(result)

&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 직접 짜보고 나면&lt;span&gt;&amp;nbsp;&lt;/span&gt;MultiQueryRetriever&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 라이브러리가 무엇을 대신해주는지 분명해진다. 결국 사용자가 던진 하나의 질문을 LLM으로 여러 개의 유사 질문으로 확장해 검색해주는 도구인데, 그 동작은 앞서 설명한 다중 질의 생성을 단계별로 자동화한 것에 지나지 않는다. 안에서 벌어지는 일을 풀어보면, 먼저 LLM이 원래 질문을 바탕으로 표현이 다른 질문 서너 개를 만들어내고(질문 확장), 그 질문들로 각각 벡터 DB를 따로 검색한 다음(개별 검색), 찾아온 문서들을 모두 합쳐 중복을 걷어내고(결과 통합), 그렇게 넓어진 맥락을 LLM에 넘겨 최종 답변을 만든다. 앞에서 손으로 그렸던 그림을 라이브러리가 그대로 대신 밟아주는 셈이다.&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;그래서 다중 질의는 언제 쓰고, 언제는 과한가&quot; data-ke-size=&quot;size26&quot;&gt;그래서 다중 질의는 언제 쓰고, 언제는 과한가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여기까지 보면 다중 질의를 무조건 깔아야 할 것 같지만, 공짜로 얻는 개선은 아니다. 득과 실을 같이 놓고 봐야 판단이 선다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;| 장점 | 단점 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;|---|---|&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;|&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;검색 누락 방지&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 여러 표현으로 검색망을 넓히니 관련 문서를 찾을 확률이 크게 올라간다 |&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;비용 발생&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 질문을 확장하려고 LLM을 한 번 더 호출하므로 API 비용이 추가된다 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;|&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;사용자 편의성&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 대충 물어도 AI가 알아서 의도를 파악해 검색해준다 |&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;속도 저하&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 검색을 여러 번 돌리는 만큼 전체 응답 시간이 길어진다 |&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정리하면 다중 질의는 사용자의 질문이 막연하고 표현이 제각각인 상황, 그래서 단어 하나 어긋났다고 정답 문서를 통째로 놓치는 게 가장 뼈아픈 서비스에서 값을 한다. 검색 한 번 더 도는 지연과 LLM 추가 호출 비용을 내주고, 검색 누락을 막는 안전망을 사는 거래라고 봤다. 반대로 질문이 이미 정형화돼 있거나(사내 코드명&amp;middot;정해진 양식의 질의처럼) 응답 속도와 호출 비용이 빠듯한 환경이라면, 모든 질문을 굳이 서너 갈래로 불려가며 검색할 이유는 없다. 결국 다중 질의는 기본값으로 켜두는 기능이 아니라, 내 사용자가 얼마나 엉성하게 묻는지를 먼저 보고 켜고 끄는 선택지에 가깝다고 볼수있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RAG 마스터 레시피 - 브라이스 유 , 조경아 , 박수진 , 김재웅&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216240484&quot;&gt;https://product.kyobobook.co.kr/detail/S000216240484&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테디노트의 랭체인을 활용한 RAG 비법노트&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216574552&quot;&gt;https://product.kyobobook.co.kr/detail/S000216574552&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI Engineering/RAG</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/197</guid>
      <comments>https://kimback03.tistory.com/197#entry197comment</comments>
      <pubDate>Sun, 21 Jun 2026 17:00:55 +0900</pubDate>
    </item>
    <item>
      <title>RAG 성능 고도화 &amp;mdash; 청킹 전략 깊게 파기</title>
      <link>https://kimback03.tistory.com/196</link>
      <description>&lt;h1 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;RAG 성능 고도화1 &amp;mdash; 청킹 전략 깊게 파기&quot;&gt;RAG 성능 고도화 &amp;mdash; 청킹 전략 깊게 파기&lt;/h1&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 RAG 기반 시스템을 실제로 구축하면서 마주쳤던 청킹 전략의 선택 문제를 정리한 내용이다. 책과 Gemini의 도움을 빌려 궁금증을 해소해가며 쌓은 기록이다.&amp;nbsp;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;청킹이 왜 필요한가&quot; data-ke-size=&quot;size26&quot;&gt;청킹이 왜 필요한가?&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RAG 시스템을 처음 설계할 때 가장 단순한 접근은 &quot;그냥 문서를 통째로 LLM에 넣어서 처리하면 되지 않나?&quot;라는 생각이 든다. 하지만 실제로 해보면 두 가지 벽에 부딪힌다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫째는 토큰 한계다. LLM은 한 번에 처리할 수 있는 텍스트 양이 정해져 있어서, 문서가 길면 아예 입력 자체가 불가능하다. 둘째는 정확도 문제다. 설령 들어간다 해도, 방대한 문서 속에서 관련 없는 내용까지 한꺼번에 읽어야 하는 LLM은 핵심을 놓치거나 엉뚱한 부분에 집중하기 쉽다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 문서를 적절한 크기로 잘라 저장하고, 필요한 조각만 골라 LLM에 넘기는 방식이 등장했다. 이 잘라내는 행위를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;청킹(Chunking)&lt;/b&gt;이라 부른다. 어떻게 자르느냐에 따라 검색 정확도와 답변 품질이 크게 달라지기 때문에, 단순해 보이는 이 과정이 사실 RAG 고도화의 핵심 중 하나다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;청킹 전략은 크게 두 갈래로 나뉜다.&lt;br /&gt;&lt;b&gt;문자수 기반 분할&lt;/b&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;부모-자식 기반 분할&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;등이 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;문자수 기반 분할 &amp;mdash; 가장 단순하지만 한계가 있다&quot; data-ke-size=&quot;size26&quot;&gt;문자수 기반 분할 &amp;mdash; 가장 단순하지만 한계가 있다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자수 기반 분할은 말 그대로 정해진 글자 수마다 텍스트를 잘라내는 방식이다. 구현이 간단하고 예측 가능하다는 장점이 있다. 하지만 문장 한가운데를 무 자르듯 끊어버리면, 앞뒤 문맥을 잃어버려 임베딩 모델이 의미를 제대로 포착하지 못하는 문제가 생긴다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 완화하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;오버랩(Overlap)&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;개념이 쓰인다. 앞 청크의 끝부분과 뒷 청크의 앞부분이 일정 글자 수만큼 겹치도록 설정해, 문맥의 연결 고리를 유지하는 방식이다. 예를 들어&lt;span&gt;&amp;nbsp;&lt;/span&gt;chunk_size=500,&lt;span&gt;&amp;nbsp;&lt;/span&gt;chunk_overlap=50으로 설정하면 청크들이 50자씩 겹치며 이어진다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;[원본 문서] ─────────────────────────────────────────────────&amp;gt;
[Chunk 1: 0~500자] ──(Overlap: 50자)── [Chunk 2: 450~950자] ──&amp;gt; ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangChain의&lt;span&gt;&amp;nbsp;&lt;/span&gt;CharacterTextSplitter가 이 방식의 대표 구현체다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;# 텍스트 청킹 예제: 도서관 책 소개 텍스트를 chunk_size와 chunk_overlap으로 분할

def chunk_text(text: str, chunk_size: int, chunk_overlap: int) -&amp;gt; list[str]:
    &quot;&quot;&quot;텍스트를 일정 크기(chunk_size)로 나누되, 앞 청크와 겹치는 구간(chunk_overlap)을 유지한다.&quot;&quot;&quot;
    if chunk_size &amp;lt;= 0:
        raise ValueError(&quot;chunk_size는 1 이상이어야 합니다.&quot;)
    if chunk_overlap &amp;gt;= chunk_size:
        raise ValueError(&quot;chunk_overlap은 chunk_size보다 작아야 합니다.&quot;)

    chunks = []
    start = 0

    while start &amp;lt; len(text):
        end = start + chunk_size
        chunks.append(text[start:end])

        # 다음 청크 시작점: chunk_size만큼 전진하되 overlap만큼 뒤로 당긴다
        start += chunk_size - chunk_overlap

    return chunks


# 도서관 책 소개 예시 텍스트
book_description = (
    &quot;도서관 사서가 추천하는 이번 달의 책은 '내일의 도서관'입니다. &quot;
    &quot;이 책은 작은 마을 도서관을 배경으로, 책 속 이야기가 현실로 스며드는 신비로운 경험을 그립니다. &quot;
    &quot;주인공 소율은 오래된 열람실 한 켠에서 낡은 일기장을 발견하고, &quot;
    &quot;그 일기장의 주인을 찾아가는 여정을 통해 독서의 의미를 깨달아 갑니다. &quot;
    &quot;저자는 도서관이 단순한 책 보관소가 아니라 사람과 이야기가 만나는 공간임을 섬세하게 묘사합니다. &quot;
    &quot;책장을 넘길수록 독자는 자신만의 도서관 기억을 떠올리게 될 것입니다.&quot;
)

# 청킹 파라미터 설정
CHUNK_SIZE = 80      # 한 청크에 담을 최대 문자 수
CHUNK_OVERLAP = 20   # 앞 청크와 겹칠 문자 수

chunks = chunk_text(book_description, CHUNK_SIZE, CHUNK_OVERLAP)

print(f&quot;전체 텍스트 길이: {len(book_description)}자&quot;)
print(f&quot;chunk_size={CHUNK_SIZE}, chunk_overlap={CHUNK_OVERLAP}&quot;)
print(f&quot;생성된 청크 수: {len(chunks)}\n&quot;)

for i, chunk in enumerate(chunks):
    print(f&quot;[청크 {i+1}] (길이: {len(chunk)}자)&quot;)
    print(chunk)
    print(&quot;-&quot; * 60)
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;단순하고 빠르다는 건 장점이지만, 문장 구조나 단락 경계를 전혀 고려하지 않는다는 게 치명적인 약점이다. 이 단점을 개선한 것이 뒤에서 다룰&lt;span&gt;&amp;nbsp;&lt;/span&gt;RecursiveCharacterTextSplitter다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;부모-자식 분할 &amp;mdash; 검색과 답변을 분리한다&quot; data-ke-size=&quot;size26&quot;&gt;부모-자식 분할 &amp;mdash; 검색과 답변을 분리한다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자수 기반이 &quot;어떻게 자르냐&quot;의 문제였다면, 부모-자식 분할은 &quot;무엇을 검색에 쓰고, 무엇을 LLM에 넘기냐&quot;를 분리하는 전략이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심 아이디어는 이렇다. 검색할 때는 잘게 쪼갠&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자식 청크&lt;/b&gt;로 정밀하게 찾고, LLM에 답변을 생성시킬 때는 충분한 문맥을 담은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;부모 청크&lt;/b&gt;를 넘긴다. 두 역할을 서로 다른 크기의 텍스트에 맡기는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구체적인 흐름은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자의 질문이 들어오면 벡터 DB에서 유사한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자식 청크&lt;/b&gt;를 검색한다.&lt;/li&gt;
&lt;li&gt;찾아낸 자식 청크에 붙어있는 메타데이터(parent_id)를 따라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;부모 청크&lt;/b&gt;를 추적한다.&lt;/li&gt;
&lt;li&gt;부모 청크의 풍부한 원문을 LLM에 전달해 답변을 생성한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자식 청크는 임베딩되어 벡터 DB에 저장하고, 부모 청크는 RDB 형태로 원본을 보관한다. 단순 의미 기반 검색을 대체하는 게 아니라, 상호보완적으로 활용하는 구조다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;청크 사이즈는 어떻게 정하는가&quot; data-ke-size=&quot;size26&quot;&gt;청크 사이즈는 어떻게 정하는가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;부모-자식 구조를 도입하기로 했다면, 각각의 크기를 어떻게 설정할지가 다음 고민이다. 기준은 단순하다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자식은 &quot;검색 정확도&quot;를, 부모는 &quot;답변의 풍부함&quot;을 기준으로&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;크기를 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분추천 크기역할&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;자식(Child)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;200 ~ 400자&lt;/td&gt;
&lt;td&gt;검색 전용. 임베딩 모델이 의미를 가장 잘 포착하는 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;부모(Parent)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;1,000 ~ 2,000자&lt;/td&gt;
&lt;td&gt;답변 생성용. LLM이 충분한 문맥을 파악할 수 있는 단락 단위&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자식이 너무 작으면 의미가 파편화되고, 너무 크면 검색 정밀도가 떨어진다. 부모가 너무 크면 LLM에 전달되는 토큰이 급증해 비용이 오르고 속도가 느려진다. 보통 자식 크기의 4~5배 수준이 실무에서 쓰는 경험치다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;임베딩 모델의 토큰 한계를 놓치면 안 된다&quot; data-ke-size=&quot;size26&quot;&gt;임베딩 모델의 토큰 한계를 놓치면 안 된다&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;청크 사이즈를 정할 때 한 가지 더 고려해야 하는 게 있다. 바로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;임베딩 모델이 한 번에 처리할 수 있는 최대 토큰 수&lt;/b&gt;다.기존의 오픈소스 임베딩 모델은 BERT 구조를 기반으로 만들어졌는데, 이 모델들은 설계 단계에서 &quot;최대 512개 토큰까지만 처리한다&quot;고 약속하고 학습됐다. 한글 기준으로 대략 700~900자 분량이다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;전통적으로 많은 경량 오픈소스 임베딩 모델은 BERT 계열 인코더 구조라, 설계상 최대 512 토큰까지만 처리하도록 학습된 경우가 많다(한글 약 700~900자). 다만 최근에는 nomic-embed&amp;middot;jina&amp;middot;BGE-M3처럼 8,192 토큰을 지원하는 오픈소스 임베딩도 늘었고, 상위권은 디코더 LLM 기반으로 옮겨가는 추세다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 자식 청크를 900자로 설정했는데 임베딩 모델의 한계가 512토큰이라면, 모델은 앞의 512토큰만 읽고 나머지는 그냥 잘라낸다. 결과적으로 청크 뒷부분의 내용은 벡터에 반영되지 않는다. 사용자의 질문에 대한 정답이 그 잘린 부분에 있었다면, 검색 자체에서 누락된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 OpenAI의&lt;span&gt;&amp;nbsp;&lt;/span&gt;text-embedding-3&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 상용 임베딩 모델은 최대 8,191토큰까지 처리할 수 있어 이 한계에서 훨씬 자유롭다. 다만 검색 정밀도를 위해서는 상용 모델이라도 자식 청크를 작게 유지하는 게 유리하다는 건 마찬가지다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;BERT와 GPT는 같은 LLM인데 왜 역할이 다른가&quot; data-ke-size=&quot;size26&quot;&gt;BERT와 GPT는 같은 LLM인데 왜 역할이 다른가&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;청킹 공부를 하다 보면 BERT가 자주 등장한다. GPT도 LLM이고 BERT도 LLM의 범주에 들어가는데, 왜 임베딩 이야기를 할 때마다 BERT가 튀어나오는지 처음엔 헷갈렸다. 이유는 두 모델이 트랜스포머(Transformer)라는 공통 뿌리에서 출발했지만, 서로 완전히 다른 방향으로 특화됐기 때문이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPT는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Decoder&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;구조로, &quot;다음 단어 예측하기&quot;에 올인했다. 앞의 문장을 보고 뒤에 올 내용을 생성하는 데 특화된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;'작가'&lt;/b&gt;다. 우리가 흔히 쓰는 생성형 AI의 주인공이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;BERT는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Encoder&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;구조로, &quot;문맥 파악하기&quot;에 올인했다. 문장 전체를 한꺼번에 읽어서 각 단어의 의미를 숫자로 뽑아내는 데 특화된&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;'독해 전문가'&lt;/b&gt;다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GPT에게 &quot;사과&quot;를 주면 &amp;rarr; &quot;사과는 맛있는 과일입니다...&quot;라고 글을 이어 쓴다.&lt;/li&gt;
&lt;li&gt;BERT에게 &quot;사과&quot;를 주면 &amp;rarr;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[0.12, -0.05, 0.88, ...]&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 임베딩 벡터를 뱉는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;RAG에서 정보를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;'찾는'&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;단계에는 글을 잘 쓰는 능력보다 의미를 정확한 숫자로 바꿔 유사도를 계산하는 능력이 필요하다. 그래서 검색 역할은 BERT 계열 모델들이 담당하는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실무에서는 이 두 유형을 구분해서 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유형대표 모델역할&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;임베딩 모델 (BERT 계열)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;BGE, KoSBERT 등&lt;/td&gt;
&lt;td&gt;문장을 벡터로 변환. 검색 담당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;생성 LLM (GPT, Claude 계열)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;GPT-4o, Claude, Llama&lt;/td&gt;
&lt;td&gt;찾은 정보를 읽고 답변 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;임베딩 모델 선택: BERT 계열 vs text-embedding-3&quot; data-ke-size=&quot;size26&quot;&gt;임베딩 모델 선택: BERT 계열 vs text-embedding-3&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오픈소스 BERT 계열 모델과 OpenAI의 상용 임베딩 모델 중 무엇을 쓸지는 상황에 따라 갈린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분BERT 계열 (BGE, RoBERTa 등)text-embedding-3 (OpenAI)&lt;/p&gt;
&lt;table style=&quot;color: #000000; text-align: start; border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인프라&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;내 서버(GPU)에 직접 올려야 함&lt;/td&gt;
&lt;td&gt;API 키로 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;입력 한계&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;보통 512 토큰&lt;/td&gt;
&lt;td&gt;최대 8,191 토큰&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;비용&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;하드웨어 유지비&lt;/td&gt;
&lt;td&gt;API 호출당 과금&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;데이터가 외부로 나가지 않음&lt;/td&gt;
&lt;td&gt;OpenAI 서버로 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;text-embedding-3는 긴 문서도 한 번에 처리할 수 있고, 벡터 차원을 줄여도 성능 하락이 적은 Matryoshka Embedding 기능이 있어 저장 비용을 아낄 수 있다. MTEB 리더보드 상위권에 꾸준히 이름을 올리며 한국어 성능도 준수하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반면 BERT 계열을 고집하는 이유는 명확하다. 금융권이나 공공기관처럼 데이터를 외부로 보낼 수 없는 환경이라면 선택의 여지가 없다. 또 네트워크 통신 없이 내 GPU 안에서 바로 계산하므로, 초당 수천 건의 검색이 필요한 서비스에서는 로컬 모델이 유리하다. 의료, 법률처럼 전문 용어가 많은 도메인에서는 해당 분야 데이터로 직접 파인튜닝한 BERT 계열 모델이 범용 모델보다 훨씬 정확하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;BERT에서 파생된 주요 모델들도 알아두면 좋다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;SBERT(Sentence-BERT)&lt;/b&gt;는 문장 간 유사도 검색에 최적화된 버전으로, 우리가 쓰는 대부분의 오픈소스 임베딩이 이 구조를 따른다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;BioBERT&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;LegalBERT&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;KoSBERT&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 도메인 특화 모델도 있고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;DistilBERT&lt;/b&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;ALBERT&lt;/b&gt;처럼 성능은 유지하면서 크기를 줄인 경량화 버전도 있다.&lt;/p&gt;
&lt;blockquote style=&quot;color: #000000; text-align: start;&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대 AI 개발의 표준은 모델을 직접 만드는 게 아니라, 이미 거대한 데이터로 학습된 Pre-trained 임베딩 모델을 가져다 쓰는 것이다. Hugging Face의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://huggingface.co/spaces/mteb/leaderboard&quot; data-tooltip-position=&quot;top&quot;&gt;MTEB 리더보드&lt;/a&gt;에서 내 도메인(한국어, 금융, 의료 등)에 점수가 높은 모델을 선택하면 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;RecursiveCharacterTextSplitter &amp;mdash; 문맥을 지키며 자르는 법&quot; data-ke-size=&quot;size26&quot;&gt;RecursiveCharacterTextSplitter &amp;mdash; 문맥을 지키며 자르는 법&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;문자수 기반 분할의 단점, 즉 문장 중간을 무작정 끊어버리는 문제를 개선한 것이&lt;span&gt;&amp;nbsp;&lt;/span&gt;RecursiveCharacterTextSplitter다. LangChain에서 가장 기본적으로 쓰이는 분할기로, AI 기술이 들어간 게 아니라 순수한 Python 로직이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;CharacterTextSplitter보다 한 단계 진화된 형태라고 보면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;핵심 원칙은 &quot;글자 수는 맞추되, 문맥은 깨지 마라&quot;다. 단순히 500자마다 칼로 자르는 대신,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;문단과 문장의 경계를 최대한 존중하면서&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;지정한 크기에 맞게 양보해가며 잘라낸다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내부적으로는 구분자 배열&lt;span&gt;&amp;nbsp;&lt;/span&gt;[&quot;\n\n&quot;, &quot;\n&quot;, &quot; &quot;, &quot;&quot;]을 순서대로 시도한다. 먼저 문단 단위(\n\n)로 잘라보고 크기가 맞으면 확정하고, 문단이 너무 크면 문장 단위(\n)로 다시 쪼개고, 그래도 크면 단어 단위, 마지막에는 글자 단위까지 내려간다. 덕분에 가능한 한 의미 있는 단위를 유지하면서 청킹된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;ParentDocumentRetriever &amp;mdash; 부모-자식 구조의 실전 구현&quot; data-ke-size=&quot;size26&quot;&gt;ParentDocumentRetriever &amp;mdash; 부모-자식 구조의 실전 구현&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;LangChain은 부모-자식 청킹 전략을 쉽게 쓸 수 있도록&lt;span&gt;&amp;nbsp;&lt;/span&gt;ParentDocumentRetriever를 제공한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;두 개의 저장소와 두 개의 분할기를 유기적으로 연결한 데이터 매핑 라우터&lt;/b&gt;다. 반드시 이걸 써야 하는 건 아니지만, 빠르게 프로토타입을 만들 때는 편리하다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;구성 요소는 네 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;vectorstore&lt;/b&gt;: 자식 청크와 그 임베딩 벡터를 저장하는 벡터 DB. 검색 엔진 역할.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;docstore&lt;/b&gt;: 부모 문서 원본을 보관하는 Key-Value 저장소(InMemoryStore, Redis 등). 검색이 아닌 원문 보관 목적.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;child_splitter&lt;/b&gt;: 자식 청크를 자르는 기준. 유사도 검색 정밀도를 높이기 위해 작게(100~200자) 설정.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;parent_splitter&lt;/b&gt;: 부모 청크를 자르는 기준. LLM이 읽기 좋은 크기(1,000~2,000자)로 설정. 지정하지 않으면 원본 문서 전체가 부모가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;vim&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;import math
import uuid
from collections import defaultdict

# ── 간단한 TF 기반 벡터라이저 (실제 임베딩 대신 사용) ──────────────────────
def tokenize(text: str) -&amp;gt; list[str]:
    return text.lower().split()

def build_vocab(documents: list[str]) -&amp;gt; list[str]:
    vocab = set()
    for doc in documents:
        vocab.update(tokenize(doc))
    return sorted(vocab)

def vectorize(text: str, vocab: list[str]) -&amp;gt; list[float]:
    tokens = tokenize(text)
    freq = defaultdict(int)
    for t in tokens:
        freq[t] += 1
    return [freq.get(word, 0) for word in vocab]

def cosine_similarity(vec_a: list[float], vec_b: list[float]) -&amp;gt; float:
    dot = sum(a * b for a, b in zip(vec_a, vec_b))
    norm_a = math.sqrt(sum(a ** 2 for a in vec_a))
    norm_b = math.sqrt(sum(b ** 2 for b in vec_b))
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return dot / (norm_a * norm_b)


# ── 부모 저장소(RDB 역할): 뉴스 기사 전체 원문 ─────────────────────────────
parent_store: dict[str, dict] = {}

# ── 자식 저장소(벡터 DB 역할): 문단 단위 청크 ─────────────────────────────
child_store: dict[str, dict] = {}
vocab: list[str] = []


def split_into_paragraphs(article_text: str) -&amp;gt; list[str]:
    return [p.strip() for p in article_text.split(&quot;\n\n&quot;) if p.strip()]


def index_article(title: str, content: str, date: str) -&amp;gt; str:
    parent_id = str(uuid.uuid4())

    # 부모 저장: 기사 원문 전체 보관
    parent_store[parent_id] = {&quot;title&quot;: title, &quot;content&quot;: content, &quot;date&quot;: date, &quot;paragraph_ids&quot;: []}

    # 자식 생성: 문단 단위로 분리
    for para in split_into_paragraphs(content):
        child_id = str(uuid.uuid4())
        parent_store[parent_id][&quot;paragraph_ids&quot;].append(child_id)
        child_store[child_id] = {&quot;text&quot;: para, &quot;parent_id&quot;: parent_id, &quot;vector&quot;: None}

    return parent_id


def rebuild_index():
    global vocab
    vocab = build_vocab([c[&quot;text&quot;] for c in child_store.values()])
    for child in child_store.values():
        child[&quot;vector&quot;] = vectorize(child[&quot;text&quot;], vocab)


def search(query: str, top_k: int = 3) -&amp;gt; list[dict]:
    query_vec = vectorize(query, vocab)
    scores = [(cid, cosine_similarity(query_vec, c[&quot;vector&quot;])) for cid, c in child_store.items()]
    scores.sort(key=lambda x: x[1], reverse=True)

    seen_parents: set[str] = set()
    results: list[dict] = []
    for child_id, sim in scores:
        if len(results) &amp;gt;= top_k:
            break
        parent_id = child_store[child_id][&quot;parent_id&quot;]
        if parent_id in seen_parents:
            continue
        seen_parents.add(parent_id)
        article = parent_store[parent_id]
        results.append({
            &quot;score&quot;: round(sim, 4),
            &quot;matched_paragraph&quot;: child_store[child_id][&quot;text&quot;],
            &quot;title&quot;: article[&quot;title&quot;],
            &quot;date&quot;: article[&quot;date&quot;],
            &quot;full_content&quot;: article[&quot;content&quot;],
        })
    return results
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;LangChain을 걷어내고 직접 구현하는 이유&quot; data-ke-size=&quot;size26&quot;&gt;LangChain을 걷어내고 직접 구현하는 이유&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제로 상용 수준의 서비스를 만들다 보면, 시니어 엔지니어들이 결국&lt;span&gt;&amp;nbsp;&lt;/span&gt;ParentDocumentRetriever를 버리고 직접 구현하는 경우를 자주 보게 된다. 이유는 세 가지다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;벡터 DB 고유 기능의 완전한 활용&lt;/b&gt;이다. LangChain 래퍼를 통하면 Qdrant 같은 벡터 DB가 지원하는 고성능 기능을 쓰기 까다로워진다. 직접 구현하면 테넌트 격리(user_id&lt;span&gt;&amp;nbsp;&lt;/span&gt;기반 필터링 등)를 자유롭게 쿼리에 녹여낼 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;두 번째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;인프라 단순화&lt;/b&gt;다. LangChain은 내부에&lt;span&gt;&amp;nbsp;&lt;/span&gt;InMemoryStore&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 불안정한 자원을 쓰거나, 자체 규격 객체로 데이터를 변환하는 오버헤드가 있다. 내가 이미 쓰는 메인 DB(Postgres, Redis 등)에 부모 문서를 올려버리면 구조가 훨씬 단순해지고 백업과 관리도 쉬워진다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;세 번째는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;LangChain 버전 업데이트 지옥 탈출&lt;/b&gt;이다. LangChain은 API 변경이 잦아 버전이 조금만 바뀌어도 기존 코드가 깨지는 것으로 악명이 높다. 비즈니스의 핵심인 RAG 파이프라인을 순수 Python과 공식 DB 드라이버(qdrant-client,&lt;span&gt;&amp;nbsp;&lt;/span&gt;SQLAlchemy&lt;span&gt;&amp;nbsp;&lt;/span&gt;등)로만 짜두면, 외부 라이브러리 변화에 흔들리지 않는 견고한 구조가 된다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;직접 구현하면 쿼리 흐름은 단 두 번의 DB 호출로 정리된다. 벡터 DB에서 자식 청크로 위치를 저격하고, 그 parent_id로 메인 DB에서 부모 원문을 꺼내 LLM에 넘긴다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;import math
import random

# ── 가짜 임베딩 함수 (실제 모델 대신 해시 기반 벡터) ──────────────────────
def fake_embed(text: str, dim: int = 8) -&amp;gt; list[float]:
    random.seed(hash(text) % (2**32))
    vec = [random.gauss(0, 1) for _ in range(dim)]
    norm = math.sqrt(sum(v**2 for v in vec))
    return [v / norm for v in vec]

def cosine_similarity(a: list[float], b: list[float]) -&amp;gt; float:
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x**2 for x in a))
    norm_b = math.sqrt(sum(x**2 for x in b))
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return dot / (norm_a * norm_b)


# ── 메인 DB (SQLAlchemy 역할): 영화 리뷰 원문 저장 ─────────────────────────
parent_store: dict[str, dict] = {}

def db_insert_parent(parent_id: str, title: str, full_review: str, rating: float):
    parent_store[parent_id] = {&quot;parent_id&quot;: parent_id, &quot;title&quot;: title, &quot;full_review&quot;: full_review, &quot;rating&quot;: rating}

def db_get_parent(parent_id: str) -&amp;gt; dict | None:
    &quot;&quot;&quot;두 번째 DB 호출: parent_id로 원문 조회&quot;&quot;&quot;
    return parent_store.get(parent_id)


# ── 벡터 DB (Qdrant 역할): 자식 청크 + parent_id 저장 ─────────────────────
child_store: list[dict] = []

def vector_db_insert_child(chunk_id: str, parent_id: str, chunk_text: str):
    child_store.append({&quot;chunk_id&quot;: chunk_id, &quot;parent_id&quot;: parent_id,
                        &quot;chunk_text&quot;: chunk_text, &quot;embedding&quot;: fake_embed(chunk_text)})

def vector_db_search(query: str, top_k: int = 3) -&amp;gt; list[dict]:
    &quot;&quot;&quot;첫 번째 DB 호출: 쿼리와 유사한 자식 청크 검색&quot;&quot;&quot;
    query_vec = fake_embed(query)
    scored = [{**c, &quot;score&quot;: cosine_similarity(query_vec, c[&quot;embedding&quot;])} for c in child_store]
    scored.sort(key=lambda x: x[&quot;score&quot;], reverse=True)
    return scored[:top_k]


def parent_child_rag_search(query: str, top_k: int = 2) -&amp;gt; list[dict]:
    &quot;&quot;&quot;
    [1차 DB 호출] 벡터 DB(Qdrant): 자식 청크로 위치 저격
    [2차 DB 호출] 메인 DB(SQLAlchemy): parent_id로 부모 원문 조회
    &quot;&quot;&quot;
    # 첫 번째 DB 호출
    matched_chunks = vector_db_search(query, top_k=top_k)

    # parent_id 중복 제거
    unique_parent_ids = list(dict.fromkeys(c[&quot;parent_id&quot;] for c in matched_chunks))

    # 두 번째 DB 호출
    results = []
    for parent_id in unique_parent_ids:
        parent_doc = db_get_parent(parent_id)
        if parent_doc:
            results.append(parent_doc)

    return results
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;&amp;quot;결국 원문 전체를 저장하는 꼴 아닌가?&amp;quot;&quot; data-ke-size=&quot;size26&quot;&gt;&quot;결국 원문 전체를 저장하는 꼴 아닌가?&quot;&lt;/h2&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;부모-자식 전략을 처음 접하면 이런 의문이 생긴다. 어차피 부모에 원문 다 넣으면 문서 전체를 저장하는 거 아닌가? 맞다. 데이터의 절대적인 총량만 보면 결국 텍스트 전체를 어딘가에 보관하는 건 맞다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이 전략의 진짜 가치는 저장하느냐 마느냐가 아니라,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;검색하는 뇌(벡터 DB)와 답변하는 뇌(LLM)가 바라보는 크기를 영리하게 분리&lt;/b&gt;하는 데 있다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한 가지 오해를 먼저 짚어두자. 부모가 &quot;책 한 권 전체&quot;일 필요는 없다. 100페이지짜리 문서를 통째로 부모로 지정하면 LLM이 읽다가 토큰이 폭발하거나 엉뚱한 답을 낸다. 현업에서는 보통 이렇게 계층을 설계한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;원본 문서&lt;/b&gt;: 100페이지 전체 PDF (약 50,000자)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;부모 청크&lt;/b&gt;: 하나의 개념을 다루는 '절' 단위 (약1,000~2,000자)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식 청크&lt;/b&gt;: 몇 문장짜리 초소형 조각 (약 200자)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;부모도 결국 &quot;LLM이 한눈에 읽고 완전히 이해할 수 있는 맥락의 최소 단위&quot;로 잘려진 원문 조각이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 구조가 만들어내는 실질적인 효과는 비용이다. 벡터 DB에는 가볍고 뾰족한 자식 벡터만 쌓아 유지비와 검색 속도를 극단적으로 아끼고, 메인 DB에는 압축률이 좋은 텍스트 형태의 부모 원문을 보관한다. 사용자가 질문하면 자식 벡터로 빠르고 저렴하게 &quot;정확한 위치&quot;만 찾아낸 뒤, LLM에 프롬프트를 넘길 때만 부모 원문을 꺼내 포장한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결과적으로 문서 전체를 매번 LLM에 읽게 만드는 구조에 비해,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;서버 비용은 크게 줄이면서도 LLM이 엉뚱한 대답을 하지 못하도록 완벽한 전후 맥락을 쥐여주는 구조&lt;/b&gt;가 완성된다. 검색의 해상도를 높이기 위해 껍데기 레이어(자식 벡터)를 한 겹 더 만들고 메인 데이터(부모 원문)와 링크를 걸어둔 것이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;br /&gt;RAG 마스터 레시피 - 브라이스 유 , 조경아 , 박수진 , 김재웅&lt;br /&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216240484&quot;&gt;https://product.kyobobook.co.kr/detail/S000216240484&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;테디노트의 랭체인을 활용한 RAG 비법노트&lt;br /&gt;&lt;a href=&quot;https://product.kyobobook.co.kr/detail/S000216574552&quot;&gt;https://product.kyobobook.co.kr/detail/S000216574552&lt;/a&gt;&lt;/p&gt;</description>
      <category>AI Engineering/RAG</category>
      <category>AI</category>
      <category>LangChain</category>
      <category>Rag</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/196</guid>
      <comments>https://kimback03.tistory.com/196#entry196comment</comments>
      <pubDate>Sun, 21 Jun 2026 16:51:57 +0900</pubDate>
    </item>
    <item>
      <title>LLM모델과 토크나이저와 임베딩모델</title>
      <link>https://kimback03.tistory.com/195</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 로드하고, 자르고, 저장하고, 검색하는 일반적인 RAG 개발 과정에서 필요한 개념을 학습한다. 우리가 보편적으로 사용하는 LLM은 사전 학습단계를 거쳐 만들어진 그야말로 범용 모델이다. 우리가 쓰는 언어는 텍스트로 표현된다. 언어는 비정형데이터로서 컴퓨터가 이해하기 힘들다. 그렇기 때문에 텍스트를 일종의 단어사전(맵핑지도)을 통해 수치화 하는 과정이 필요한데, 여기서 필요한것이 토크나이저 이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;토크나이저는 내부 규칙에 의해서 텍스트를 자르고, 이것을 숫자로 변환한다. 이 토크나이저로 자르고 변환하는 규칙은 각각의 토크나이저별로 다르기 때문에, 반드시 LLM과 세트로 써야한다. 예를들어 BERT 모델을 쓰면서 OPENAPI의 토크나이저를 쓰면 바보가 된다. 학습시 사용했던 토크나이저 방식으로 적용해야한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;&amp;quot;토크나이저와 임베딩모델은 뭔 차이?&amp;quot;&quot; data-ke-size=&quot;size23&quot;&gt;&quot;토크나이저와 임베딩모델은 뭔 차이?&quot;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 토크나이저 (Tokenizer): &quot;단어에 부여하는 단순한 '고유번호'&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;텍스트를 적당한 크기(Token)로 쪼개고, 각 조각에 고유한 정수 ID(Index 번호)를 매겨주는 작업이다. 토크나이저는 그저 사전에 등록된 순서대로 번호를 붙일 뿐이다. 번호만 봐서는 103번(사과)과 904번(바나나)이 비슷한 과일인지, 22번(자동차)이 전혀 다른 물건인지 기계는 절대 알 수 없다. 즉, 의미를 파악할 수 없다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토크나이저 거친 후:&lt;span&gt;&amp;nbsp;&lt;/span&gt;[103],&lt;span&gt;&amp;nbsp;&lt;/span&gt;[904],&lt;span&gt;&amp;nbsp;&lt;/span&gt;[22]&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 임베딩 모델 (Embedding Model): &quot;단어의 의미를 담은 '좌표'&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;'토크나이저'가 만든 정수ID들을 입력으로 받아서, 그 단어가 가진 '의미(Context)'를 다차원 공간의 실수 벡터(좌표)로 변환해 주는 모델이다. 이 벡터 좌표에는 '깊은 의미와 관계'가 담겨 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;임베딩 거친 후: 사과&lt;span&gt;&amp;nbsp;&lt;/span&gt;[0.9, 0.1, -0.2], 자동차&lt;span&gt;&amp;nbsp;&lt;/span&gt;[-0.9, 0.8, 0.9]&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리가 보기에는 그냥 숫자일 뿐, 의미를 파악할 수 없다. 이 둘은 경쟁하거나 선택하는 관계가 아니라, 순서대로 이어지는 하나의 파이프라인이다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;사과를 먹는다&quot;를 입력하면 텍스트가 토크나이저를 거쳐&lt;span&gt;&amp;nbsp;&lt;/span&gt;[103, 15, 88]&lt;span&gt;&amp;nbsp;&lt;/span&gt;같은 정수 행렬이 되고, 임베딩 모델을 거쳐 실수 벡터 좌표로 변환되어 벡터 데이터베이스에 들어가게 된다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot; ️ 토크나이저 및 임베딩 테스트 코드&quot; data-ke-size=&quot;size20&quot;&gt; ️ 토크나이저 및 임베딩 테스트 코드&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;# 필요한 라이브러리 설치: pip install openai transformers torch
from transformers import AutoTokenizer
from openai import OpenAI
import os
from dotenv import load_dotenv

load_dotenv()

# 1. 토크나이저 테스트 (HuggingFace Open Source 기준)
tokenizer = AutoTokenizer.from_pretrained(&quot;bert-base-multilingual-cased&quot;)
text = &quot;사과를 먹는다&quot;

tokens = tokenizer.tokenize(text)
input_ids = tokenizer.convert_tokens_to_ids(tokens)

print(f&quot;--- [Step 1: Tokenizer] ---&quot;)
print(f&quot;원본 텍스트: {text}&quot;)
print(f&quot;쪼개진 토큰: {tokens}&quot;)
print(f&quot;정수 인덱스(ID): {input_ids}\n&quot;)

# 2. 임베딩 모델 테스트 (OpenAI API 기준)
client = OpenAI(api_key=os.getenv(&quot;OPENAI_API_KEY&quot;))

response = client.embeddings.create(
    input=text,
    model=&quot;text-embedding-3-small&quot;
)

embedding_vector = response.data[0].embedding

print(f&quot;--- [Step 2: Embedding] ---&quot;)
print(f&quot;임베딩 벡터 차원 수: {len(embedding_vector)}&quot;)
print(f&quot;벡터 샘플(앞 5개): {embedding_vector[:5]}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot;&amp;quot;그럼 임베딩 모델과 LLM 모델은 상관없이 다르게 써도 되나?&amp;quot;&quot; data-ke-size=&quot;size23&quot;&gt;&quot;그럼 임베딩 모델과 LLM 모델은 상관없이 다르게 써도 되나?&quot;&lt;/h3&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;결론부터 말하면, 완벽하게 독립적이다. 전혀 상관없다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;OpenAI의 임베딩 모델로 문서를 검색하고, 찾아낸 문서를 바탕으로 다른 LLM모델이 답변을 생성하게 하는 것은 완전히 가능하며, 실제로 현업 RAG 아키텍처에서 아주 흔하게 쓰이는 디커플링(Decoupling) 패턴이다. 헷갈릴 수 있지만, LLM과 임베딩모델은 세트가 아니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내부 사정 : &quot;순수 텍스트(Plain Text)로 전달한다&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;임베딩 모델과 생성 모델(LLM/SLM)이 만나는 지점에는 '벡터(숫자)'가 오가지 않는다. 오직 '사람이 읽을 수 있는 글자'만 오간다. 임베딩 모델이 질문을 벡터로 바꾼 뒤 DB를 서칭하면, Vector DB가 가장 유사도 높은 문서 조각을 찾아낸다. 이때 꺼내는 것은 벡터 숫자가 아니라, 원래 저장되어 있던 순수 텍스트다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Q. &quot;어떻게 숫자로 검색했는데, 텍스트가 나오는가?&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 Vector DB의 저장 방식은 &quot;라벨과 내용물을 같이 넣는다&quot;. 처음 문서를 DB에 넣을 때, 내부에는 사실 여러 데이터가 한 쌍(Row)으로 묶여서 들어간다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;ID:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;doc_001&lt;span&gt;&amp;nbsp;&lt;/span&gt;(문서 고유 번호)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Vector:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;[0.12, -0.55, 0.89 ...]&lt;span&gt;&amp;nbsp;&lt;/span&gt;(임베딩 모델이 만들어준 숫자 좌표)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Document:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;제3조: 법인카드 식대 한도는 3만 원이다.&quot;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(원본 텍스트, Payload)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Metadata:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;{&quot;부서&quot;: &quot;영업부&quot;, &quot;연도&quot;: 2024}&lt;span&gt;&amp;nbsp;&lt;/span&gt;(필터용)&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말해 숫자를 보고 해당하는 문장을 맵핑해서 리턴한다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;저장된 Payload 상에 있는 텍스트를 직접 조회할 수 있나?&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;가능하다. 굳이 AI나 임베딩 모델을 거치지 않고도, 전통적인 데이터베이스(RDB)처럼 저장된 텍스트(Payload)를 마음대로 조회하고 필터링할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;AI 검색용&lt;span&gt;&amp;nbsp;&lt;/span&gt;.query():&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;임베딩 모델이 작동하여 수학적 거리를 계산한다. (벡터 기반 유사도 검색)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;관리자 조회용&lt;span&gt;&amp;nbsp;&lt;/span&gt;.get():&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;벡터 계산을 전혀 하지 않고 내부의 SQLite를 이용해 일반적인 SQL의&lt;span&gt;&amp;nbsp;&lt;/span&gt;SELECT&lt;span&gt;&amp;nbsp;&lt;/span&gt;문법처럼 조건에 맞는 텍스트와 메타데이터만 뽑아온다. (조건 기반 정확도 검색)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-heading=&quot; ️ Vector DB Payload 및 조회 테스트 코드&quot; data-ke-size=&quot;size20&quot;&gt; ️ Vector DB Payload 및 조회 테스트 코드&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot; style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;import chromadb

# 1. 크로마 DB 클라이언트 설정 (메모리 모드)
client = chromadb.Client()
collection = client.create_collection(name=&quot;office_rules&quot;)

# 2. 데이터 삽입 (ID, Vector, Document, Metadata 한 쌍)
collection.add(
    ids=[&quot;doc_001&quot;, &quot;doc_002&quot;],
    embeddings=[[0.1, 0.2, 0.3], [0.7, 0.8, 0.9]], 
    metadatas=[{&quot;category&quot;: &quot;식대&quot;, &quot;year&quot;: 2024}, {&quot;category&quot;: &quot;휴가&quot;, &quot;year&quot;: 2024}],
    documents=[&quot;제3조: 법인카드 식대 한도는 3만 원이다.&quot;, &quot;제5조: 연차 휴가는 15일이다.&quot;]
)

print(&quot;=== [Data Stored Successfully] ===\n&quot;)

#   Case 1: AI 검색 (.query) -&amp;gt; 벡터 유사도 기반
search_res = collection.query(
    query_embeddings=[[0.12, 0.21, 0.33]], 
    n_results=1
)
print(f&quot;--- [1. AI Query Result] ---&quot;)
print(f&quot;검색된 본문: {search_res['documents'][0][0]}&quot;)
print(f&quot;메타데이터: {search_res['metadatas'][0][0]}\n&quot;)

#  ️ Case 2: 관리자 조회 (.get) -&amp;gt; 메타데이터 필터 기반
get_res = collection.get(
    where={&quot;category&quot;: &quot;휴가&quot;}
)
print(f&quot;--- [2. Admin Get Result] ---&quot;)
print(f&quot;필터링된 본문: {get_res['documents'][0]}&quot;)
print(f&quot;연결된 ID: {get_res['ids'][0]}&quot;)
&lt;/code&gt;&lt;/pre&gt;</description>
      <category>AI Engineering/기본지식</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/195</guid>
      <comments>https://kimback03.tistory.com/195#entry195comment</comments>
      <pubDate>Fri, 24 Apr 2026 16:56:40 +0900</pubDate>
    </item>
    <item>
      <title>코딩강의6화 - flutter dart 앱개발 상속, 확장에 대해서 with gpt</title>
      <link>https://kimback03.tistory.com/194</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP3hSv/btsMSRIZ3aT/7w38Uzy5bXXClpPxytVLI1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP3hSv/btsMSRIZ3aT/7w38Uzy5bXXClpPxytVLI1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP3hSv/btsMSRIZ3aT/7w38Uzy5bXXClpPxytVLI1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP3hSv%2FbtsMSRIZ3aT%2F7w38Uzy5bXXClpPxytVLI1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault.webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/vnM2J-Bagb0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/vnM2J-Bagb0&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=vnM2J-Bagb0&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bSewPJ/hyYuoui8QS/ZY6aOUpIQK9V6ZhR7OshN0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=74_74_1186_226,https://scrap.kakaocdn.net/dn/NiTqq/hyYvqSAStK/T2wYDbbN6qjUyPn9V1NqT1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=74_74_1186_226&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;코드와트6화 - flutter dart 확장 (상속) &amp;quot;뭘 물려받는다고?&amp;quot;&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/vnM2J-Bagb0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*아래의 글은 유튜브 코드와트 채널에서 연재되는 강의를 글로 작성한 내용입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*대화형식으로 진행되며, ai chat GPT를 활용한 코딩이 핵심입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*시대에 맞게 코딩.개발의 조력자로써 ai를 잘 활용것이 목표입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 코드와트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 확장에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;확장(extends), 상속(&lt;b&gt;Inheritance)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 대부분의 언어들은 extends 확장(또는 상속) 이라고 하는 기능을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장은 말그대로 기존의 것을 가지고 확대, 확장 한다는 의미가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전을 살펴 보겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.08.27.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X3ngd/btsMTg2vN5f/09Wioq8EXmQTpazgxtHitK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X3ngd/btsMTg2vN5f/09Wioq8EXmQTpazgxtHitK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X3ngd/btsMTg2vN5f/09Wioq8EXmQTpazgxtHitK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX3ngd%2FbtsMTg2vN5f%2F09Wioq8EXmQTpazgxtHitK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;892&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.08.27.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전을 찾아보니 우리가 오늘 해야할 일이 무엇인지 명확해 집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 실행해보죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561642420&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class A {
	var name = 'A';
	void testFunc(){
		print('클래스 A 테스트 입니다.');
	}
}

class B extends A {
	@override
	var name = 'B';

	@override
	void testFunc(){
		print('클래스 B 테스트 입니다.');
	}

}

main (){
	var b = B();
	b.testFunc();
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A 라는 클래스, B라는 클래스가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B 클래스를 만들때 extends A 라는 키워드가 보입니다. 눈치 채셨겠지만 이게 부분이 바로 오늘 배울 확장(상속) 키워드를 사용하는 부분입니다. extends A 라고 명시하면 A에 있는 변수, 함수등을 B에서 가져다 사용할수 있는것이죠. B를 다시 보니 특이한게 보입니다. 바로 override 라고 하는 키워드 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.19.42.png&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIxcVU/btsMSQcdrc6/fWXM001kblrga74aAOyuM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIxcVU/btsMSQcdrc6/fWXM001kblrga74aAOyuM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIxcVU/btsMSQcdrc6/fWXM001kblrga74aAOyuM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIxcVU%2FbtsMSQcdrc6%2FfWXM001kblrga74aAOyuM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;948&quot; height=&quot;602&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.19.42.png&quot; data-origin-width=&quot;948&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;override 는 기존의 A라는 클래스에 작성된 name, testFunc 를 가져와서 B에서 새롭게 구현하는것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이것을 재정의 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 A 라는 클래스에 정의된 변수명과 함수명을 가져와서 B에 상황에 맞게 재정의하여 사용한다. 라는 말입니다. 물론 재정의 하지 않고, 그대로 A의 기능을 물려(사용)받을수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt; &amp;zwj;♀️&amp;nbsp;&amp;ldquo;물려받는다고요?&amp;rdquo;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 확장이라고 썻지만, 다른 관점에서 상속이라는 말로 표현하기도 합니다. extends 라는 키워드를 통해서 만들어지는 A-B의 관계가 마치 부모(Super)-자식(Sub) 간의 관계가 형성되는것 처럼 보이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt; &amp;zwj;♀️&amp;nbsp;&amp;rdquo;쌤 상속? 확장??? &amp;hellip; 부모-자식관계는 또 뭐죠?&amp;rdquo;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 이해하기에 어려운 내용이 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 gpt 를 통해서 자세하게 한번 알아볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.55.12.png&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NIgvl/btsMS3WJk6L/8RRJLJQJEtZRuaxP7XTmY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NIgvl/btsMS3WJk6L/8RRJLJQJEtZRuaxP7XTmY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NIgvl/btsMS3WJk6L/8RRJLJQJEtZRuaxP7XTmY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNIgvl%2FbtsMS3WJk6L%2F8RRJLJQJEtZRuaxP7XTmY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;869&quot; height=&quot;490&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.55.12.png&quot; data-origin-width=&quot;869&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.29.45.png&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TIirA/btsMUkXhC6E/Ym1oPAE4BAVXYcVuOUWARk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TIirA/btsMUkXhC6E/Ym1oPAE4BAVXYcVuOUWARk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TIirA/btsMUkXhC6E/Ym1oPAE4BAVXYcVuOUWARk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTIirA%2FbtsMUkXhC6E%2FYm1oPAE4BAVXYcVuOUWARk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1686&quot; height=&quot;1178&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.29.45.png&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.30.40.png&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s0TVV/btsMTCK8Uit/2XAf55xIh1HE1zxxMKUM61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s0TVV/btsMTCK8Uit/2XAf55xIh1HE1zxxMKUM61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s0TVV/btsMTCK8Uit/2XAf55xIh1HE1zxxMKUM61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs0TVV%2FbtsMTCK8Uit%2F2XAf55xIh1HE1zxxMKUM61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1680&quot; height=&quot;1050&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.30.40.png&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.30.57.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djRziB/btsMSbON0Ua/Fx5PmCgk7v05SmLIBo1uaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djRziB/btsMSbON0Ua/Fx5PmCgk7v05SmLIBo1uaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djRziB/btsMSbON0Ua/Fx5PmCgk7v05SmLIBo1uaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjRziB%2FbtsMSbON0Ua%2FFx5PmCgk7v05SmLIBo1uaK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1694&quot; height=&quot;1168&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.30.57.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.31.28.png&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsucGu/btsMUmgt4DC/B8PR8wAF0UXiqgXGb5VaK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsucGu/btsMUmgt4DC/B8PR8wAF0UXiqgXGb5VaK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsucGu/btsMUmgt4DC/B8PR8wAF0UXiqgXGb5VaK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsucGu%2FbtsMUmgt4DC%2FB8PR8wAF0UXiqgXGb5VaK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1710&quot; height=&quot;916&quot; data-filename=&quot;스크린샷 2025-03-09 오후 3.31.28.png&quot; data-origin-width=&quot;1710&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B라는 클래스는 A 클래스를 extends 함으로써 클래스의 재사용성이 올라갑니다. 쉽게 말해서 똑같은 기능을 하는 코드를 여기저기 만들지 말고 extends를 통해 기존의 기능을 사용하거나, 확장하는 형태로 만들어 나갈수 있는것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터는 좀더 이해를 돕기위해 동물들을 빗대어 작성해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개와 고양이는 동물입니다. 그리고 숨쉬기를 하고있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 확장의 기능이 없다면 이런식의 중복 되는 기능들이 많아질수 있겠죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561796931&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Dog{

	void breathe(){
		print('숨쉬기...');
	}
}

class Cat{

	void breathe(){
		print('숨쉬기...');
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561806295&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal{
	void breathe(){
		print('숨쉬다...');
	}
}

class Dog extends Animal{
}

class Cat extends Animal{
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장의 기능을 사용하면 Dog, Cat 친구들에게 굳이 또 숨쉬는 기능을 만들어줘야 할 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Animal 이라고 하는 부모(super) 로 부터 숨쉬는 기능을 물려받고 사용하기만 되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Dog와 Cat은 자연스럽게 Animal의 자식(Sub)이 되는거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561825792&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main(){
	var dog = Dog();
	var cat = Cat();
	dog.breathe();
	cat.breathe();

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 여기서 개와 고양이만의 각각 고유한 숨쉬기 방법을 부여해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 필요한것이 바로 override(재정의) 라는 키워드 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 부모클래스인 Animal 은 고칠필요가 없구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개와 고양이에서 각각의 숨쉬기 방법을 정의해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561844028&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Dog extends Animal{
	@override
	void breathe(){
		print('개의 숨소리');
	}
}

class Cat extends Animal{
	@override
	void breathe(){
		print('고양이의 숨소리');
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 마음대로 정의한 숨쉬기 방법이니, 이부분은 심각하게 생각하지 말아주세요. 그게 중요한게 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드를 보고 실행해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561861701&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal{
	void breathe(){
		print('숨쉬다...');
	}
}

class Dog extends Animal{
	@override
	void breathe(){
		print('개의 숨소리');
	}
}

class Cat extends Animal{
	@override
	void breathe(){
		print('고양이의 숨소리');
	}
}

main(){
	var dog = Dog();
	var cat = Cat();
	dog.breathe();
	cat.breathe();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가요? 개와 고양이만에 숨쉬는 방법이 만들어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇가지 더 만들어보겠습니다. 새를 만들어 볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561876255&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Bird extends Animal{
	@override
	void breathe(){
		print('새의 숨소리');
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새를 만들었으니 main 함수에도 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561888030&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main(){
	var dog = Dog();
	var cat = Cat();
	var bird = Bird();
	dog.breathe();
	cat.breathe();
	bird.breathe();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt; &amp;zwj;♂️&amp;nbsp;&amp;rdquo;오&amp;hellip; 그러면 동물들이 많아질수록 모든 코드를 다 작성하고 실행해야 되나요?&amp;rdquo;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞습니다. 동물들이 많아질수록 당연히 코드작성이 많아지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 좀 더 유연하게 대처해보겠습니다. 함수를 하나 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수는 매개변수(Parameter)로 Animal 타입(부모)을 전달받고 (숫자와 문자만 전달할수 있는게 아닙니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;animal 클래스에 작성된 breathe를 사용하는 간단한 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AnimalBreathe 라는 임의의 클래스를 만들고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그안에 Dog, Cat의 부모인 Animal 타입을 매개변수로 받는 breathe 함수를 만들어줬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561918971&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class AnimalBreathe(){
	void breathe(Animal animal){  
		animal.breathe();
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AnimalBreathe 라고 하는 모든 동물의 숨쉬기 실행자를 생성하고 Dog 를 생성했습니다. 그리고 나서 dog 를 breathe를 호출할때 같이 전달해주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561933029&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main(){
	var animalBreathe = AnimalBreathe();
	var dog = Dog();
	
	animalBreathe.breathe(dog);  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742561945833&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal{
	void breathe(){
		print('숨쉬다...');
	}
}

class Dog extends Animal{
	@override
	void breathe(){
		print('개의 숨소리');
	}
}

class Cat extends Animal{
	@override
	void breathe(){
		print('고양이의 숨소리');
	}
}

class AnimalBreathe{
	void breathe(Animal animal){
		animal.breathe();
	}
}

main(){
	var animalBreathe = AnimalBreathe();
	var dog = Dog();
	var cat = Cat();
	
	animalBreathe.breathe(dog);
	animalBreathe.breathe(cat);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 함으로써 breathe 라는 숨쉬는 기능을 각각의 동물별로 구분하여 &lt;b&gt;직접&lt;/b&gt; 실행해줄 필요없이 어떤동물을 만들던 그저 animalBreathe (&lt;b&gt;위임&lt;/b&gt;) 의 함수에 전달하는것 만으로 각 동물들의 고유한 숨소리가 작동하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 구문이지만 사실 여기에는 &lt;b&gt;상속, 다형성, 업캐스팅&lt;/b&gt; 등 많은 지식들이 복합적으로 결합된 결과물 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽지만 이부분은 뒤에서 좀더 자세하게 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Mobile Dev/코드와트 코딩강의</category>
      <category>IOS</category>
      <category>개발</category>
      <category>안드로이드</category>
      <category>앱개발</category>
      <category>코딩</category>
      <category>플러터</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/194</guid>
      <comments>https://kimback03.tistory.com/194#entry194comment</comments>
      <pubDate>Fri, 14 Mar 2025 15:07:45 +0900</pubDate>
    </item>
    <item>
      <title>헤이젠 hey-gen 을 이용한 나만의 ai 아바타 캐릭터 만들기</title>
      <link>https://kimback03.tistory.com/193</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kKthz/btsMTjrutJX/1ESfnG5rnRkIuasrkkh8NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kKthz/btsMTjrutJX/1ESfnG5rnRkIuasrkkh8NK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kKthz/btsMTjrutJX/1ESfnG5rnRkIuasrkkh8NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkKthz%2FbtsMTjrutJX%2F1ESfnG5rnRkIuasrkkh8NK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1652&quot; height=&quot;950&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ai탐험대 시리즈 입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 컨텐츠 제작할때&amp;nbsp; 활용도가 높은 아바타 생성 플랫폼을 사용해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;1. d-id 무료사용기&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2. heygen 유료사용기&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컨텐츠는 유튜브를 통해서도 볼수있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/qc2TCRtul9I&quot;&gt;https://youtu.be/qc2TCRtul9I&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=qc2TCRtul9I&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/cVNSZO/hyYrOOdeTW/0oF7yYkmICzc2MVwW4NieK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=182_46_1050_532,https://scrap.kakaocdn.net/dn/uTV8E/hyYvkdAqZQ/bokVQPaDFLjbI48bzrRcq0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=182_46_1050_532&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;[ai 탐험대] heygen으로 놀고 먹고 일하는 아바타 만들기&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/qc2TCRtul9I&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;d-id&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://studio.d-id.com&quot;&gt;https://studio.d-id.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 플랫폼에 접속해주시구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.51.17.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;845&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNzEwr/btsMJp5Af6P/OeFK6chU6Gp49EXZlmp64K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNzEwr/btsMJp5Af6P/OeFK6chU6Gp49EXZlmp64K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNzEwr/btsMJp5Af6P/OeFK6chU6Gp49EXZlmp64K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNzEwr%2FbtsMJp5Af6P%2FOeFK6chU6Gp49EXZlmp64K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;570&quot; height=&quot;376&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.51.17.png&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;845&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아바타 메뉴로 이동합니다.&lt;br /&gt;상단에 보시면 이렇게 두가지 방식으로 개인의 아바타를 만들수 있는데요. 우선은 기본적으로 제공되는 아바타를 이용해볼께요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.51.30.png&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MV0nB/btsMIcTD3Mv/WxdKq9GlqxKn4qkxVxUAtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MV0nB/btsMIcTD3Mv/WxdKq9GlqxKn4qkxVxUAtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MV0nB/btsMIcTD3Mv/WxdKq9GlqxKn4qkxVxUAtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMV0nB%2FbtsMIcTD3Mv%2FWxdKq9GlqxKn4qkxVxUAtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;236&quot; height=&quot;115&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.51.30.png&quot; data-origin-width=&quot;236&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.51.50.png&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;353&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY5K5s/btsMJqXHEgO/fRB0cnjKODXDxqQ5YMhJck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY5K5s/btsMJqXHEgO/fRB0cnjKODXDxqQ5YMhJck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY5K5s/btsMJqXHEgO/fRB0cnjKODXDxqQ5YMhJck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY5K5s%2FbtsMJqXHEgO%2FfRB0cnjKODXDxqQ5YMhJck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;188&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.51.50.png&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;353&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤 해보시면 하단에 많은 인물을 고를수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.11.png&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cekqxW/btsMIK3wwJu/tWy0aNR1WaGS7oZQ8MYmNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cekqxW/btsMIK3wwJu/tWy0aNR1WaGS7oZQ8MYmNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cekqxW/btsMIK3wwJu/tWy0aNR1WaGS7oZQ8MYmNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcekqxW%2FbtsMIK3wwJu%2FtWy0aNR1WaGS7oZQ8MYmNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;664&quot; height=&quot;331&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.11.png&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주근깨가 매력적인 엠마로 선택할께요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.26.png&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXuPhE/btsMH4VU1Zu/p6iPFFRu5KMTDujzNDMw9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXuPhE/btsMH4VU1Zu/p6iPFFRu5KMTDujzNDMw9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXuPhE/btsMH4VU1Zu/p6iPFFRu5KMTDujzNDMw9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXuPhE%2FbtsMH4VU1Zu%2Fp6iPFFRu5KMTDujzNDMw9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;419&quot; height=&quot;321&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.26.png&quot; data-origin-width=&quot;419&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.33.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4v020/btsMG8xWs2c/1MravUYTUDpIVJug3lRPLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4v020/btsMG8xWs2c/1MravUYTUDpIVJug3lRPLk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4v020/btsMG8xWs2c/1MravUYTUDpIVJug3lRPLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4v020%2FbtsMG8xWs2c%2F1MravUYTUDpIVJug3lRPLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;440&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.33.png&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아바타 비디오를 선택합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴듯한 편집창이 나왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.52.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;930&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clSSbe/btsMINMJbUI/SuckFvkTmqkEweP7UZxuVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clSSbe/btsMINMJbUI/SuckFvkTmqkEweP7UZxuVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clSSbe/btsMINMJbUI/SuckFvkTmqkEweP7UZxuVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclSSbe%2FbtsMINMJbUI%2FSuckFvkTmqkEweP7UZxuVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;448&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.52.52.png&quot; data-origin-width=&quot;1364&quot; data-origin-height=&quot;930&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 표정정도 선택해볼수 있구요. 이곳에서 텍스트나 도형등을 이용하여 강의 영상, ppt 영상등 간단한 편집도 할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 동작도 셋팅이 가능합니다. 음성도 바꿀수 있구요.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.53.59.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;147&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwsHdm/btsMJla7bf8/YkQRy8kNf4JmZ5uMq9iY3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwsHdm/btsMJla7bf8/YkQRy8kNf4JmZ5uMq9iY3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwsHdm/btsMJla7bf8/YkQRy8kNf4JmZ5uMq9iY3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwsHdm%2FbtsMJla7bf8%2FYkQRy8kNf4JmZ5uMq9iY3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;119&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.53.59.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;147&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.54.05.png&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;115&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baqwrC/btsMIxi1OMH/pNBtgDomy96kprA50br3M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baqwrC/btsMIxi1OMH/pNBtgDomy96kprA50br3M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baqwrC/btsMIxi1OMH/pNBtgDomy96kprA50br3M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaqwrC%2FbtsMIxi1OMH%2FpNBtgDomy96kprA50br3M0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;115&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.54.05.png&quot; data-origin-width=&quot;567&quot; data-origin-height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음에 드는 음성을 선택하세요. &lt;br /&gt;그리고 나서 하단에 play 버튼을 눌러보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.54.30.png&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2LNYI/btsMHqrDYEg/Yw150snVXSkPvnXiVr6ebK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2LNYI/btsMHqrDYEg/Yw150snVXSkPvnXiVr6ebK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2LNYI/btsMHqrDYEg/Yw150snVXSkPvnXiVr6ebK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2LNYI%2FbtsMHqrDYEg%2FYw150snVXSkPvnXiVr6ebK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;212&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.54.30.png&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이고 경고창이 뜨네요. 우리가 너무 성급했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽 메뉴바에 보시면 스크립트가 있구요. 해당 메뉴로 이동하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bz0b2/btsMJolif0T/DepK4kdaAty1LxlHEnKID0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bz0b2/btsMJolif0T/DepK4kdaAty1LxlHEnKID0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;113&quot; data-origin-height=&quot;107&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.54.47.png&quot; style=&quot;width: 44.931%; margin-right: 10px;&quot; data-widthpercent=&quot;45.46&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bz0b2/btsMJolif0T/DepK4kdaAty1LxlHEnKID0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBz0b2%2FbtsMJolif0T%2FDepK4kdaAty1LxlHEnKID0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;113&quot; height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UT88V/btsMIxpQu2Z/GKvNRZTrpmzXv2jhSNb79K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UT88V/btsMIxpQu2Z/GKvNRZTrpmzXv2jhSNb79K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;465&quot; data-origin-height=&quot;367&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.55.25.png&quot; data-widthpercent=&quot;54.54&quot; style=&quot;width: 53.9062%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UT88V/btsMIxpQu2Z/GKvNRZTrpmzXv2jhSNb79K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUT88V%2FbtsMIxpQu2Z%2FGKvNRZTrpmzXv2jhSNb79K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;465&quot; height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트를 작성하고 play 해보니 문제가 있습니다. &lt;br /&gt;한글로 스크립트를 작성하면 한국어를 할줄 아는 목소리를 적용해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.55.44.png&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;229&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JRyih/btsMH84RNWJ/k18viWYOykeDQniNx2rKw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JRyih/btsMH84RNWJ/k18viWYOykeDQniNx2rKw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JRyih/btsMH84RNWJ/k18viWYOykeDQniNx2rKw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJRyih%2FbtsMH84RNWJ%2Fk18viWYOykeDQniNx2rKw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;725&quot; height=&quot;156&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.55.44.png&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;229&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.55.58.png&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAWzCT/btsMGOsMnnZ/4qLLE7ptZECyOcPd54FzxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAWzCT/btsMGOsMnnZ/4qLLE7ptZECyOcPd54FzxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAWzCT/btsMGOsMnnZ/4qLLE7ptZECyOcPd54FzxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAWzCT%2FbtsMGOsMnnZ%2F4qLLE7ptZECyOcPd54FzxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;335&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.55.58.png&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 한국말을 잘하는 음성을 셋팅해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;country 를 korea 로 바꿔줍니다. 무료버전에서는 되는게 몇개 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 아래로 내려서 선택해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.56.30.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;119&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7K1XE/btsMHWKoSB2/zM7oVtrguB7g6kuOtxGbQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7K1XE/btsMHWKoSB2/zM7oVtrguB7g6kuOtxGbQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7K1XE/btsMHWKoSB2/zM7oVtrguB7g6kuOtxGbQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7K1XE%2FbtsMHWKoSB2%2FzM7oVtrguB7g6kuOtxGbQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;119&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.56.30.png&quot; data-origin-width=&quot;439&quot; data-origin-height=&quot;119&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굉장히 구수한 이름이네요. 저는 순복씨를 선택하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 플레이버튼을 다시 누르면 이제서야 가입하라고 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가입하고 로그인하겠습니다. (가입 로그인 절차는 생략하겠습니다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.57.36.png&quot; data-origin-width=&quot;1345&quot; data-origin-height=&quot;901&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DKDUa/btsMHXWQZIa/cOjS81401dWh4PSbNb28Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DKDUa/btsMHXWQZIa/cOjS81401dWh4PSbNb28Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DKDUa/btsMHXWQZIa/cOjS81401dWh4PSbNb28Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDKDUa%2FbtsMHXWQZIa%2FcOjS81401dWh4PSbNb28Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;437&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.57.36.png&quot; data-origin-width=&quot;1345&quot; data-origin-height=&quot;901&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 화면으로 돌아와서 플레이 해보겠습니다. 약간 어색하긴 하지만 뭐 일단 넘어가겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하단에 스피드 조절바를 이용해서 적당히 음성 속도조절도 해주시구요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.57.59.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SXveZ/btsMI75fSfn/zLobHkFhDNs0EUW7QY02P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SXveZ/btsMI75fSfn/zLobHkFhDNs0EUW7QY02P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SXveZ/btsMI75fSfn/zLobHkFhDNs0EUW7QY02P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSXveZ%2FbtsMI75fSfn%2FzLobHkFhDNs0EUW7QY02P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;137&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.57.59.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음에 든다면 이제 실제로 영상을 만들어볼께요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.58.06.png&quot; data-origin-width=&quot;431&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx7o4B/btsMJepGD7n/8nHvdrqyp9Bhcs2uENGkRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx7o4B/btsMJepGD7n/8nHvdrqyp9Bhcs2uENGkRK/img.png&quot; data-alt=&quot;날 눌러요.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx7o4B/btsMJepGD7n/8nHvdrqyp9Bhcs2uENGkRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx7o4B%2FbtsMJepGD7n%2F8nHvdrqyp9Bhcs2uENGkRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;158&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.58.06.png&quot; data-origin-width=&quot;431&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;날 눌러요.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.58.55.png&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OLjiu/btsMHnoccgS/QhJeWkvkURk072yATmdkg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OLjiu/btsMHnoccgS/QhJeWkvkURk072yATmdkg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OLjiu/btsMHnoccgS/QhJeWkvkURk072yATmdkg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOLjiu%2FbtsMHnoccgS%2FQhJeWkvkURk072yATmdkg0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;278&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.58.55.png&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적당한 이름을 주고 넘어갈께요.&lt;br /&gt;만드는데 1 크래딧이 소모 되는가 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 가입하면 테스트 해볼만큼의 적당한 크래딧이 들어있으니 걱정마세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 만들어지고 있습니다. 잠시 기다려 주시면&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.59.37.png&quot; data-origin-width=&quot;1305&quot; data-origin-height=&quot;681&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nAaub/btsMIs27RCK/rNDQK5osXZiqZqRDJfP5k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nAaub/btsMIs27RCK/rNDQK5osXZiqZqRDJfP5k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nAaub/btsMIs27RCK/rNDQK5osXZiqZqRDJfP5k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnAaub%2FbtsMIs27RCK%2FrNDQK5osXZiqZqRDJfP5k1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;668&quot; height=&quot;349&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.59.37.png&quot; data-origin-width=&quot;1305&quot; data-origin-height=&quot;681&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 어색함은 있지만 이정도면 훌륭합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.59.50.png&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp6nzn/btsMH4hhnMV/pSU47rdyCRB1YKifKHensK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp6nzn/btsMH4hhnMV/pSU47rdyCRB1YKifKHensK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp6nzn/btsMH4hhnMV/pSU47rdyCRB1YKifKHensK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp6nzn%2FbtsMH4hhnMV%2FpSU47rdyCRB1YKifKHensK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;386&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.59.50.png&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;733&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무료버전에서는 워터마크가 심하게 들어가 있습니다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도저히 쓸수 없게 해놨네요........&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희가 작성한 스크립트대로 말하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마음에 든다면 다운로드 하고 활용하면 됩니다. d-id 의 경우 적당히 맛보기 수준의 테스트만 해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;heygen&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 헤이젠으로 넘어가서 실제로 자세한 사용방법을 설명해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 제가 잘 사용하고 있는 heygen 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://app.heygen.com/home&quot;&gt;https://app.heygen.com/home&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤이젠을 이용해서 작업해볼께요. d-id 와 다르게 일종의 명령어를 이용해 디테일한 아바타 생성을 할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.11.49.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;517&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQjFtV/btsMTLVksP3/FcCHFxYBxzdHTCXgtN6VO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQjFtV/btsMTLVksP3/FcCHFxYBxzdHTCXgtN6VO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQjFtV/btsMTLVksP3/FcCHFxYBxzdHTCXgtN6VO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQjFtV%2FbtsMTLVksP3%2FFcCHFxYBxzdHTCXgtN6VO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;385&quot; height=&quot;410&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.11.49.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;517&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가입과 로그인은 따로 설명하지 않겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hey-gen은 아바타 만들기, 음성 합성등의 강점을 가지고 있습니다. 실제 한번 해보죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 가입하면 일부 크래딧을 제공합니다. 이 크레딧을 소비하여 아바타 작업을 하는 것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇번 하다보면 금방 소비되니 신중하게 해봅시다. (이메일을 돌려가면서 몇번 더 테스트 해볼수 있습니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.33.16.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;1758&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baQWL8/btsMSQQTmMq/WSsNFhocK2sfkbDP30BmDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baQWL8/btsMSQQTmMq/WSsNFhocK2sfkbDP30BmDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baQWL8/btsMSQQTmMq/WSsNFhocK2sfkbDP30BmDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaQWL8%2FbtsMSQQTmMq%2FWSsNFhocK2sfkbDP30BmDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;554&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.33.16.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;1758&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아바타로 이동해보겠습니다. 아바타는 말그대로 우리의 모습을 투영한 대상을 말하는것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hey-gen 안에서 몇가지 방법이 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ai 커맨드를 통한 대상 만들기&lt;/li&gt;
&lt;li&gt;내 자신의 아바타 만들기&lt;/li&gt;
&lt;li&gt;플랫폼에 제공되는 아바타 사용하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ai 커맨드를 통한 대상 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫번째 방법 입니다. 최근에 제가 코딩강의 영상에 많이 사용하는 방법이구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명은 뒤에서 하기로 하고 일단 실행해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.34.52.png&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUgaGM/btsMTXIbDk0/66ZVLsnM2rSKK49FJilLE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUgaGM/btsMTXIbDk0/66ZVLsnM2rSKK49FJilLE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUgaGM/btsMTXIbDk0/66ZVLsnM2rSKK49FJilLE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUgaGM%2FbtsMTXIbDk0%2F66ZVLsnM2rSKK49FJilLE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;162&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.34.52.png&quot; data-origin-width=&quot;388&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아바타로 이동해보겠습니다. 아바타는 말그대로 우리의 모습을 투영한 대상을 말하는것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hey-gen 안에서 몇가지 방법이 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ai 커맨드를 통한 대상 만들기&lt;/li&gt;
&lt;li&gt;내 자신의 아바타 만들기&lt;/li&gt;
&lt;li&gt;플랫폼에 제공되는 아바타 사용하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ai 커맨드를 통한 대상 만들기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫번째 방법 입니다. 최근에 제가 코딩강의 영상에 많이 사용하는 방법이구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명은 뒤에서 하기로 하고 일단 실행해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csDQNG/btsMSUeutpC/R7echG1tll1c06CHA6ER1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csDQNG/btsMSUeutpC/R7echG1tll1c06CHA6ER1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csDQNG/btsMSUeutpC/R7echG1tll1c06CHA6ER1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsDQNG%2FbtsMSUeutpC%2FR7echG1tll1c06CHA6ER1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;463&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 버튼을 눌러 진입합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 우리가 해볼것은 Generate 입니다. 해당 기능은 커맨드 입력을 통해 ai를 만들어내는 메뉴입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.38.48.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;1376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZwxAr/btsMTJpOMgr/yHRbSCdNLHNfwrqq82G7Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZwxAr/btsMTJpOMgr/yHRbSCdNLHNfwrqq82G7Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZwxAr/btsMTJpOMgr/yHRbSCdNLHNfwrqq82G7Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZwxAr%2FbtsMTJpOMgr%2FyHRbSCdNLHNfwrqq82G7Uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;468&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.38.48.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 아바타를 만들어낼지 머릿속으로 상상해보세요. 그리고 내용을 입력하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 아시아계의 미녀스타일 아바타로 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 영문으로 작성하는것이 좋을듯 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.41.21.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;1896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKDGG8/btsMSljudzI/xNUehB7wX3TCBpe8nD7OoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKDGG8/btsMSljudzI/xNUehB7wX3TCBpe8nD7OoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKDGG8/btsMSljudzI/xNUehB7wX3TCBpe8nD7OoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKDGG8%2FbtsMSljudzI%2FxNUehB7wX3TCBpe8nD7OoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;752&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.41.21.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;1896&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영작이 익숙치 않을경우 gpt 의 도움을 받도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.46.20.png&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mTCbg/btsMSRPNA9s/Oc17Tt7Ny7ggUcpgCJLKBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mTCbg/btsMSRPNA9s/Oc17Tt7Ny7ggUcpgCJLKBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mTCbg/btsMSRPNA9s/Oc17Tt7Ny7ggUcpgCJLKBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmTCbg%2FbtsMSRPNA9s%2FOc17Tt7Ny7ggUcpgCJLKBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1082&quot; height=&quot;148&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.46.20.png&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.46.56.png&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o8YWu/btsMSEiCPaO/qXh4f4Y2R3mMhyKdVkrLfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o8YWu/btsMSEiCPaO/qXh4f4Y2R3mMhyKdVkrLfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o8YWu/btsMSEiCPaO/qXh4f4Y2R3mMhyKdVkrLfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo8YWu%2FbtsMSEiCPaO%2FqXh4f4Y2R3mMhyKdVkrLfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1660&quot; height=&quot;350&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.46.56.png&quot; data-origin-width=&quot;1660&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.48.50.png&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0YN1Q/btsMTKIUIRs/yeUmmqyQZvCLp1a4j028Nk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0YN1Q/btsMTKIUIRs/yeUmmqyQZvCLp1a4j028Nk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0YN1Q/btsMTKIUIRs/yeUmmqyQZvCLp1a4j028Nk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0YN1Q%2FbtsMTKIUIRs%2FyeUmmqyQZvCLp1a4j028Nk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1026&quot; height=&quot;156&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.48.50.png&quot; data-origin-width=&quot;1026&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.49.26.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ct1yhS/btsMTL8Ruo9/OZgzkv51G4vkaOvGDUSb80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ct1yhS/btsMTL8Ruo9/OZgzkv51G4vkaOvGDUSb80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ct1yhS/btsMTL8Ruo9/OZgzkv51G4vkaOvGDUSb80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fct1yhS%2FbtsMTL8Ruo9%2FOZgzkv51G4vkaOvGDUSb80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1634&quot; height=&quot;1180&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.49.26.png&quot; data-origin-width=&quot;1634&quot; data-origin-height=&quot;1180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 길게 작성되어있지만&amp;hellip; 자 이대로 한번 생성해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현실감있는 아바타 생성을 위해서 스타일은 리얼리스틱으로 선택합니다. 잠시 기다리면 아바타 프리뷰를 볼수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국의 대학생스러운 여성분이 나타났습니다. 첫번째 사진은 얼굴 비율이 커보입니다. 가끔씩 대두 스타일의 아바타가 튀어나와 놀랄때가 있습니다. 테스트를 위해 몇번더 generate again 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 바가 있다면 좀더 세부적인 설명이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.55.20.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ8XEG/btsMUoeh0X8/KOy7w9BjE1wwfkeKm5rQhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ8XEG/btsMUoeh0X8/KOy7w9BjE1wwfkeKm5rQhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ8XEG/btsMUoeh0X8/KOy7w9BjE1wwfkeKm5rQhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ8XEG%2FbtsMUoeh0X8%2FKOy7w9BjE1wwfkeKm5rQhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;138&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.55.20.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;138&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.56.55.png&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c1BTPm/btsMTjEWYFn/TmNu70RbBJkzYUHqagKpZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c1BTPm/btsMTjEWYFn/TmNu70RbBJkzYUHqagKpZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1BTPm/btsMTjEWYFn/TmNu70RbBJkzYUHqagKpZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1BTPm%2FbtsMTjEWYFn%2FTmNu70RbBJkzYUHqagKpZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;510&quot; height=&quot;72&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.56.55.png&quot; data-origin-width=&quot;510&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.57.37.png&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/88L8O/btsMTYNOtEG/CYqVE3ikKdm2E3ye7O85F1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/88L8O/btsMTYNOtEG/CYqVE3ikKdm2E3ye7O85F1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/88L8O/btsMTYNOtEG/CYqVE3ikKdm2E3ye7O85F1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F88L8O%2FbtsMTYNOtEG%2FCYqVE3ikKdm2E3ye7O85F1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;306&quot; height=&quot;100&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.57.37.png&quot; data-origin-width=&quot;306&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;붉은 계열의 스웨터로 변경해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;녹색 신발도 신겨보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.58.44.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X6ewk/btsMRUsZVtg/788FOdDwEbzhG2MzTVNMik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X6ewk/btsMRUsZVtg/788FOdDwEbzhG2MzTVNMik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X6ewk/btsMRUsZVtg/788FOdDwEbzhG2MzTVNMik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX6ewk%2FbtsMRUsZVtg%2F788FOdDwEbzhG2MzTVNMik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;434&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.58.44.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1652&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신이 인간을 창조 해낼때 이런느낌이었을까요? (뭔소리야 ;;;;) 잠시 물한잔 마시고 오면 끝나 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;진짜 동네 옆집 사는 대학생? 인것 처럼 익숙한 외모와 풍경을 만들어줍니다. 첫번째 두번째 사진은 좀 어색한것 같습니다. 그리고 분명히 풀샷으로 옵션을 줘도, 항상 upper body 형태를 제공해주네요. 일단 계속 실행해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.59.49.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;1738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsDrrZ/btsMR2R2Q8Q/kSFDhB1nJK169NLn4VcTv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsDrrZ/btsMR2R2Q8Q/kSFDhB1nJK169NLn4VcTv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsDrrZ/btsMR2R2Q8Q/kSFDhB1nJK169NLn4VcTv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsDrrZ%2FbtsMR2R2Q8Q%2FkSFDhB1nJK169NLn4VcTv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;694&quot; data-filename=&quot;스크린샷 2025-03-11 오후 9.59.49.png&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;1738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.04.11.png&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;1738&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkQ9Cb/btsMUqb6vF5/QOQ0MviPZpf5ok6mRs4Esk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkQ9Cb/btsMUqb6vF5/QOQ0MviPZpf5ok6mRs4Esk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkQ9Cb/btsMUqb6vF5/QOQ0MviPZpf5ok6mRs4Esk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkQ9Cb%2FbtsMUqb6vF5%2FQOQ0MviPZpf5ok6mRs4Esk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;697&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.04.11.png&quot; data-origin-width=&quot;1384&quot; data-origin-height=&quot;1738&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 동일한 유형의 사진을 만들고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 이미지를 만들기 위해 랜덤한 값을 부여하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇번 하다보니 hey-gen의 경우는 같은 얼굴유형을 유지하는걸 볼수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저같이 특정 캐릭터를 만들고 외모나, 배경만 바꿔서 지속적으로 사용해야 할때 좋다고 느끼는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 한번더 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 이색적인 코스프레로 가보겠습니다. 겨울을 좋아하는 관계로&amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742559568264&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HeyGen 아바타 생성 프롬프트 (영어)
&amp;ldquo;Create a realistic digital avatar of a young Korean woman in her early 20s with a bright and cheerful smile. She has flawless, fair skin, large expressive eyes, and a delicate facial structure resembling a K-pop idol. Her long, wavy dark brown hair flows gracefully, and she is wearing light, natural makeup with a soft pink lip color.
She is dressed in a festive Santa-inspired outfit: a red velvet dress with white fur trim, a matching Santa hat, and cozy winter gloves. Her pose is playful and full of holiday spirit.
The background features a snowy ski resort with ski slopes, chair lifts, and people enjoying winter activities. There is a decorated Christmas sled and a friendly reindeer standing beside her, adding to the festive atmosphere. The scene is filled with soft snowfall, and the lighting is bright and crisp, reflecting the winter wonderland vibe.&amp;rdquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1742559585677&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;HeyGen 아바타 생성 프롬프트 (한글 번역)
&amp;ldquo;밝고 활기찬 미소를 가진 20대 초반의 한국인 여성 아바타를 생성하세요. 그녀는 맑고 깨끗한 피부, 크고 또렷한 눈, 그리고 K-pop 아이돌처럼 세련된 얼굴형을 가지고 있습니다. 길고 웨이브진 짙은 갈색 머리가 자연스럽게 흩날리며, 은은한 핑크 립컬러와 자연스러운 메이크업을 하고 있습니다.
그녀는 크리스마스 분위기의 산타 코스튬을 입고 있습니다. 빨간 벨벳 드레스에 흰색 퍼 트리밍이 달려 있고, 산타 모자를 쓴 채 겨울용 장갑을 착용하여 따뜻한 분위기를 연출합니다. 그녀의 포즈는 장난스럽고 즐거운 크리스마스 분위기를 담고 있습니다.
배경은 눈 덮인 스키장으로, 스키 슬로프와 리프트가 보이며 겨울 스포츠를 즐기는 사람들이 있습니다. 그녀의 옆에는 크리스마스 장식이 된 썰매와 귀여운 루돌프 사슴이 함께하며, 전체적인 장면에 축제 분위기를 더합니다. 부드러운 눈이 내리는 겨울 원더랜드 분위기를 연출하며, 조명은 밝고 깨끗한 겨울 느낌을 강조합니다.&amp;rdquo;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.15.00.png&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;1308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7aa6q/btsMSA1MIWW/zWBHkVwmKg7D2UicO2mKOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7aa6q/btsMSA1MIWW/zWBHkVwmKg7D2UicO2mKOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7aa6q/btsMSA1MIWW/zWBHkVwmKg7D2UicO2mKOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7aa6q%2FbtsMSA1MIWW%2FzWBHkVwmKg7D2UicO2mKOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;494&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.15.00.png&quot; data-origin-width=&quot;1504&quot; data-origin-height=&quot;1308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 대충 아무거나 선택해주고&amp;hellip; 보이스는 나중에 추가 하겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만들어진 아바타를 선택하고 add motion으로 동작을 추가해주겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.16.30.png&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwg011/btsMSQXDMQl/nLEimJRz3Iet3QZqc6VFXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwg011/btsMSQXDMQl/nLEimJRz3Iet3QZqc6VFXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwg011/btsMSQXDMQl/nLEimJRz3Iet3QZqc6VFXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbwg011%2FbtsMSQXDMQl%2FnLEimJRz3Iet3QZqc6VFXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;308&quot; height=&quot;130&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.16.30.png&quot; data-origin-width=&quot;308&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 간단한 명령이니 한글로 작성해볼께요&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.17.16.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;1070&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XawdQ/btsMS4Bhzag/Pwr4LARNiq51Ty3zJEryCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XawdQ/btsMS4Bhzag/Pwr4LARNiq51Ty3zJEryCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XawdQ/btsMS4Bhzag/Pwr4LARNiq51Ty3zJEryCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXawdQ%2FbtsMS4Bhzag%2FPwr4LARNiq51Ty3zJEryCK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;708&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.17.16.png&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;1070&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시뒤에 모션이 추가된 아바타가 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩔때는 굉장히 어색한 모션이 나오는데 이번에는 잘 나온것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모션을 좀 더 줘볼까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.19.54.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;1072&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcrrUI/btsMTDpKm2K/vMK8LdOCzcK8xmMHxBeTtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcrrUI/btsMTDpKm2K/vMK8LdOCzcK8xmMHxBeTtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcrrUI/btsMTDpKm2K/vMK8LdOCzcK8xmMHxBeTtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcrrUI%2FbtsMTDpKm2K%2FvMK8LdOCzcK8xmMHxBeTtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;380&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.19.54.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;1072&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀더 어려운 미션을 줬습니다. 미션이 어려웠는지 아무 동작도 추가가 안되었습니다. &lt;br /&gt;여기서는 간단한 모션만 추가가 가능했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 hey-gen 의 강점중 하나라고 생각되는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 만든 아바타를 기준으로 여러가지 상황별로 의상, 배경을 만들어 낼수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 얼굴은 그대로 입니다. 아바타를 생성하는데 얼굴이 바뀌면 아무 의미가 없게되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 유료 결제후에 사용할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.33.13.png&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNizXG/btsMT2CxMtm/ILgZKAgiwRAguKL7uY3K2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNizXG/btsMT2CxMtm/ILgZKAgiwRAguKL7uY3K2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNizXG/btsMT2CxMtm/ILgZKAgiwRAguKL7uY3K2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNizXG%2FbtsMT2CxMtm%2FILgZKAgiwRAguKL7uY3K2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;448&quot; height=&quot;585&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.33.13.png&quot; data-origin-width=&quot;744&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Generate looks via prompts by training a custom ai model.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행해보겠습니다. 5분정도 시간이 소요됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHLqkw/btsMSU6Dm0k/PxI6cjKQTg6RWrqr9HDfIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHLqkw/btsMSU6Dm0k/PxI6cjKQTg6RWrqr9HDfIK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;688&quot; data-filename=&quot;스크린샷 2025-03-11 오후 10.38.59.png&quot; width=&quot;473&quot; height=&quot;479&quot; style=&quot;width: 62.8308%; margin-right: 10px;&quot; data-widthpercent=&quot;63.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHLqkw/btsMSU6Dm0k/PxI6cjKQTg6RWrqr9HDfIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHLqkw%2FbtsMSU6Dm0k%2FPxI6cjKQTg6RWrqr9HDfIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beKb91/btsMTbNLvoT/c45r2QgkB1Iay39UK2lLL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beKb91/btsMTbNLvoT/c45r2QgkB1Iay39UK2lLL1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;1536&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.11.11.png&quot; style=&quot;width: 36.0064%;&quot; data-widthpercent=&quot;36.43&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beKb91/btsMTbNLvoT/c45r2QgkB1Iay39UK2lLL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeKb91%2FbtsMTbNLvoT%2Fc45r2QgkB1Iay39UK2lLL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;870&quot; height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳에서 다양한 룩을 만들어 낼수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트를 통해서 디테일한 뷰를 만들수도 있고 정해진 템플릿(세트) 으로 만들어 낼수도 있죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프트를 통해서는 작업을 해봤으니 이번에는 편하게 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.13.20.png&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wWb37/btsMT8Jqo89/ypffpgPCFd1bWXvwEKM3i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wWb37/btsMT8Jqo89/ypffpgPCFd1bWXvwEKM3i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wWb37/btsMT8Jqo89/ypffpgPCFd1bWXvwEKM3i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwWb37%2FbtsMT8Jqo89%2FypffpgPCFd1bWXvwEKM3i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;214&quot; height=&quot;112&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.13.20.png&quot; data-origin-width=&quot;214&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.13.47.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;1656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfhW1u/btsMTXajSo9/KUKYHihFmVxdoESNkLN2Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfhW1u/btsMTXajSo9/KUKYHihFmVxdoESNkLN2Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfhW1u/btsMTXajSo9/KUKYHihFmVxdoESNkLN2Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfhW1u%2FbtsMTXajSo9%2FKUKYHihFmVxdoESNkLN2Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;361&quot; height=&quot;871&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.13.47.png&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;1656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;look packs 라는 탭이 보이는데 이부분으로 진입해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 룩을 자동으로 생산해줍니다. 마음에 드는 룩으로 고른뒤 작업을 진행해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 아바타가 산타 마을에서 휴가를 즐겼으니 &amp;hellip;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 맨날 놀고먹고만 하면 재미가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에 가서 일을 시켜보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점은 20looks 을 자동으로 생성한다는것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크래딧을 잘 확인하면서 작업해야 겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어질 동안에 잠시 멤버쉽을 확인해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.18.43.png&quot; data-origin-width=&quot;2416&quot; data-origin-height=&quot;872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HZDAu/btsMRZHLES0/pWdcnSyxNKdJA59Tj4k4DK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HZDAu/btsMRZHLES0/pWdcnSyxNKdJA59Tj4k4DK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HZDAu/btsMRZHLES0/pWdcnSyxNKdJA59Tj4k4DK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHZDAu%2FbtsMRZHLES0%2FpWdcnSyxNKdJA59Tj4k4DK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;238&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.18.43.png&quot; data-origin-width=&quot;2416&quot; data-origin-height=&quot;872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.20.40.png&quot; data-origin-width=&quot;2934&quot; data-origin-height=&quot;1748&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEUs9Z/btsMSCZtmnb/nXnS95iF759174ocYVK9qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEUs9Z/btsMSCZtmnb/nXnS95iF759174ocYVK9qk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEUs9Z/btsMSCZtmnb/nXnS95iF759174ocYVK9qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEUs9Z%2FbtsMSCZtmnb%2FnXnS95iF759174ocYVK9qk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;412&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.20.40.png&quot; data-origin-width=&quot;2934&quot; data-origin-height=&quot;1748&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 Creator 멤버쉽으로 결제했구요. 아바타 몇개를 정하고 다양한 룩을 만들면서 한달 넉넉하게 쓸수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 달러가 많이 비싸진 관계로&amp;hellip; 업무상 쓰는게 아니라면 부담되는 가격이긴 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;493&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s3Eu3/btsMS51ek6t/0G735jutdpXJADkOcqaIu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s3Eu3/btsMS51ek6t/0G735jutdpXJADkOcqaIu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s3Eu3/btsMS51ek6t/0G735jutdpXJADkOcqaIu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs3Eu3%2FbtsMS51ek6t%2F0G735jutdpXJADkOcqaIu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;288&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/83bfk/btsMUl2XnEi/xJXlkvQbXh8KyXFVSrCPe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/83bfk/btsMUl2XnEi/xJXlkvQbXh8KyXFVSrCPe1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;911&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.26.45.png&quot; width=&quot;442&quot; height=&quot;367&quot; data-widthpercent=&quot;61.4&quot; style=&quot;width: 60.6856%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/83bfk/btsMUl2XnEi/xJXlkvQbXh8KyXFVSrCPe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F83bfk%2FbtsMUl2XnEi%2FxJXlkvQbXh8KyXFVSrCPe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1096&quot; height=&quot;911&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pqAcF/btsMUnGr7ZV/fcyeFvcb9XAhs6b0gRjN3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pqAcF/btsMUnGr7ZV/fcyeFvcb9XAhs6b0gRjN3K/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;788&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.26.57.png&quot; style=&quot;width: 38.1516%;&quot; data-widthpercent=&quot;38.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pqAcF/btsMUnGr7ZV/fcyeFvcb9XAhs6b0gRjN3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpqAcF%2FbtsMUnGr7ZV%2FfcyeFvcb9XAhs6b0gRjN3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.26.52.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;543&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwfBGU/btsMS3CoxGb/yqVuxrzslBR321fjr7Fp70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwfBGU/btsMS3CoxGb/yqVuxrzslBR321fjr7Fp70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwfBGU/btsMS3CoxGb/yqVuxrzslBR321fjr7Fp70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwfBGU%2FbtsMS3CoxGb%2FyqVuxrzslBR321fjr7Fp70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1346&quot; height=&quot;543&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.26.52.png&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;543&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대박입니다. 정말 편리하게 다양한 룩을 제작해줍니다. 한번 더 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 크레딧이 많이 남아서 그런게 아니라 다양한 예제를 제공해드리기 위해&amp;hellip;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 카페가서도 일 많이 하잖아요? 이번에는 카페에서 일을 시켜보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 보면 볼수록&amp;hellip;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아바타가 아이라인을 너무 두껍게 그린게 아닌가 하는 생각이&amp;hellip;&amp;hellip;(되돌릴수 없&amp;hellip;.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu7Io2/btsMTfJiJlM/8Mc6kVvpudtKcYb00vWTu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu7Io2/btsMTfJiJlM/8Mc6kVvpudtKcYb00vWTu0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;410&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.28.42.png&quot; style=&quot;width: 69.2498%; margin-right: 10px;&quot; data-widthpercent=&quot;70.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu7Io2/btsMTfJiJlM/8Mc6kVvpudtKcYb00vWTu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu7Io2%2FbtsMTfJiJlM%2F8Mc6kVvpudtKcYb00vWTu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1044&quot; height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgGI4V/btsMT0rlRR7/NY7pO7NBitoSqzKLScRuk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgGI4V/btsMT0rlRR7/NY7pO7NBitoSqzKLScRuk1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;796&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.28.48.png&quot; width=&quot;347&quot; height=&quot;319&quot; style=&quot;width: 29.5874%;&quot; data-widthpercent=&quot;29.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgGI4V/btsMT0rlRR7/NY7pO7NBitoSqzKLScRuk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgGI4V%2FbtsMT0rlRR7%2FNY7pO7NBitoSqzKLScRuk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;866&quot; height=&quot;796&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능은 대박입니다. 상당히 만족스러운 결과물을 주는것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할점이 있는데 바로 손가락 입니다. ai 가 특히나 과절이나, 손가락을 잘 못그려줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세히 확인해보는게 좋겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열심히 일했으니 체력관리 해야죠. 제가 못하는 갓생을 한번 살게 해줘보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.30.31.png&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bys02f/btsMSPj22YD/3mdCP3nJid3KiynCi9F8ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bys02f/btsMSPj22YD/3mdCP3nJid3KiynCi9F8ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bys02f/btsMSPj22YD/3mdCP3nJid3KiynCi9F8ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbys02f%2FbtsMSPj22YD%2F3mdCP3nJid3KiynCi9F8ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;348&quot; height=&quot;256&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.30.31.png&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바벨이 쇠골에 연결된?????? 무서운 사진이 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.31.05.png&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;642&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zogkA/btsMT0LEVZG/uiwt0OTC1wDWafqsKxoAr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zogkA/btsMT0LEVZG/uiwt0OTC1wDWafqsKxoAr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zogkA/btsMT0LEVZG/uiwt0OTC1wDWafqsKxoAr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzogkA%2FbtsMT0LEVZG%2Fuiwt0OTC1wDWafqsKxoAr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;283&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.31.05.png&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;642&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 바벨을 휘어버리는 파워걸......&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다 잘하는건 아닙니다. 가끔은 이런 엉터리 이미지도 생성해 냅니다만&amp;hellip;. 적당히 걸러야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 경우는 어색함이 있긴 하지만 나름대로 이미지를 잘 뽑아줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.31.40.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;840&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tadJu/btsMSn2ElPI/1R0ksGyK9BIXBiawHuXL7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tadJu/btsMSn2ElPI/1R0ksGyK9BIXBiawHuXL7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tadJu/btsMSn2ElPI/1R0ksGyK9BIXBiawHuXL7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtadJu%2FbtsMSn2ElPI%2F1R0ksGyK9BIXBiawHuXL7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;412&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.31.40.png&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덤벨을 비닐도 안뜯고 쓰는 헤이걸 입니다....ㅋㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무게를 올려야 겠습니다. (점진적과부하적용)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.32.20.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;869&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbSk6i/btsMSyCWwLt/ZP1nTpaQbEAGkaZ5o9PTu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbSk6i/btsMSyCWwLt/ZP1nTpaQbEAGkaZ5o9PTu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbSk6i/btsMSyCWwLt/ZP1nTpaQbEAGkaZ5o9PTu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbSk6i%2FbtsMSyCWwLt%2FZP1nTpaQbEAGkaZ5o9PTu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;454&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.32.20.png&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;869&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;????????????&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미친 스트랭스 입니다....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 바벨은 무슨 용도인지???????&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엉터리 사진이 출력되고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 작업하면 한계가 느껴집니다. &lt;br /&gt;사용해보시면 아시겠지만 ai가 손을 구현하는데 굉장히 어려워 합니다. &lt;br /&gt;가끔 손가락이 4개이거나 혹은 없거나 ㄷ ㄷ ㄷ ㄷ ㄷ;;; 방향이 바뀌었거나&amp;hellip;. ㄷㄷㄷㄷㄷㄷㄷ 등등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.33.36.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;471&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NWCb0/btsMT0kAwkW/nZaTwGiIoWYokVgszUUgi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NWCb0/btsMT0kAwkW/nZaTwGiIoWYokVgszUUgi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NWCb0/btsMT0kAwkW/nZaTwGiIoWYokVgszUUgi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNWCb0%2FbtsMT0kAwkW%2FnZaTwGiIoWYokVgszUUgi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1274&quot; height=&quot;471&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.33.36.png&quot; data-origin-width=&quot;1274&quot; data-origin-height=&quot;471&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요정도 선에서 라이트하게 쓸땐 좋습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.33.48.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lCvM6/btsMS8pZ6uH/Vll1WK9BZLkdxk04Am5LH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lCvM6/btsMS8pZ6uH/Vll1WK9BZLkdxk04Am5LH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lCvM6/btsMS8pZ6uH/Vll1WK9BZLkdxk04Am5LH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlCvM6%2FbtsMS8pZ6uH%2FVll1WK9BZLkdxk04Am5LH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1284&quot; height=&quot;452&quot; data-filename=&quot;스크린샷 2025-03-21 오후 9.33.48.png&quot; data-origin-width=&quot;1284&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;홈트장면을 사용해서 아바타 영상을 만들어볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.52.10.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yYzLH/btsMTXVGgL0/JVrS8Bg91BUQHlPJWLFjF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yYzLH/btsMTXVGgL0/JVrS8Bg91BUQHlPJWLFjF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yYzLH/btsMTXVGgL0/JVrS8Bg91BUQHlPJWLFjF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyYzLH%2FbtsMTXVGgL0%2FJVrS8Bg91BUQHlPJWLFjF1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;244&quot; height=&quot;244&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.52.10.png&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.52.25.png&quot; data-origin-width=&quot;3162&quot; data-origin-height=&quot;1980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sGLCe/btsMTg9jsUy/kqZt9u29SVVbsKxkCYHds1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sGLCe/btsMTg9jsUy/kqZt9u29SVVbsKxkCYHds1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sGLCe/btsMTg9jsUy/kqZt9u29SVVbsKxkCYHds1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsGLCe%2FbtsMTg9jsUy%2FkqZt9u29SVVbsKxkCYHds1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;705&quot; height=&quot;441&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.52.25.png&quot; data-origin-width=&quot;3162&quot; data-origin-height=&quot;1980&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 화면 구조입니다. 여기서 아바타를 활용한 영상을 제작할수 있습니다. 간단한 발표자료나, 강의영상도 만들수 있구요. 일단 해보죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.53.14.png&quot; data-origin-width=&quot;164&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9uwHg/btsMTFHJ3SJ/ua7Z1o7HoMld9Z59rwjKwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9uwHg/btsMTFHJ3SJ/ua7Z1o7HoMld9Z59rwjKwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9uwHg/btsMTFHJ3SJ/ua7Z1o7HoMld9Z59rwjKwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9uwHg%2FbtsMTFHJ3SJ%2Fua7Z1o7HoMld9Z59rwjKwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;164&quot; height=&quot;178&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.53.14.png&quot; data-origin-width=&quot;164&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 내용을 작성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.54.32.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;1184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJdleS/btsMT1jtmP6/WdQ7HAIGD4frGkxo6XeLy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJdleS/btsMT1jtmP6/WdQ7HAIGD4frGkxo6XeLy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJdleS/btsMT1jtmP6/WdQ7HAIGD4frGkxo6XeLy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJdleS%2FbtsMT1jtmP6%2FWdQ7HAIGD4frGkxo6XeLy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1276&quot; height=&quot;1184&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.54.32.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;1184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 그 영상에 따른 스크립트(대본)을 작성하면 되구요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.54.26.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u9Mf8/btsMSzoeugg/BAPgkrerm1feeNoEVJD52k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u9Mf8/btsMSzoeugg/BAPgkrerm1feeNoEVJD52k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u9Mf8/btsMSzoeugg/BAPgkrerm1feeNoEVJD52k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu9Mf8%2FbtsMSzoeugg%2FBAPgkrerm1feeNoEVJD52k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1254&quot; height=&quot;192&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.54.26.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.55.01.png&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coW3Tz/btsMStVYWoB/RlX9KbdQ378nqIgWG1jnik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coW3Tz/btsMStVYWoB/RlX9KbdQ378nqIgWG1jnik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coW3Tz/btsMStVYWoB/RlX9KbdQ378nqIgWG1jnik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoW3Tz%2FbtsMStVYWoB%2FRlX9KbdQ378nqIgWG1jnik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1252&quot; height=&quot;140&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.55.01.png&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플레이 버튼으로 음성을 들어보면서 테스트 해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 음성을 지원하고 있으니 테스트 해보시고 마음에 드는게 없다면 내 음성을 합성할수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.57.14.png&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sekE5/btsMSQiWQKH/K8MW3LNjKXC2nrBjBVGNkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sekE5/btsMSQiWQKH/K8MW3LNjKXC2nrBjBVGNkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sekE5/btsMSQiWQKH/K8MW3LNjKXC2nrBjBVGNkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsekE5%2FbtsMSQiWQKH%2FK8MW3LNjKXC2nrBjBVGNkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;130&quot; height=&quot;122&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.57.14.png&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.57.44.png&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;1146&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxuf1x/btsMTf3EvTZ/jKBkOxxmKRrkQGzuVfRObk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxuf1x/btsMTf3EvTZ/jKBkOxxmKRrkQGzuVfRObk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxuf1x/btsMTf3EvTZ/jKBkOxxmKRrkQGzuVfRObk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxuf1x%2FbtsMTf3EvTZ%2FjKBkOxxmKRrkQGzuVfRObk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2140&quot; height=&quot;1146&quot; data-filename=&quot;스크린샷 2025-03-12 오후 1.57.44.png&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;1146&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트를 어떻게 구성하는지에 따라서 음성에 높낮이 속도 감정 등이 달라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러번 테스트해보면서 세부적인 음성을 조절 하면 되겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.01.31.png&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;1590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QbvX6/btsMTBSWQ7Y/qDtgtsPT1xnOe3BdhAWE91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QbvX6/btsMTBSWQ7Y/qDtgtsPT1xnOe3BdhAWE91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QbvX6/btsMTBSWQ7Y/qDtgtsPT1xnOe3BdhAWE91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQbvX6%2FbtsMTBSWQ7Y%2FqDtgtsPT1xnOe3BdhAWE91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;406&quot; height=&quot;793&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.01.31.png&quot; data-origin-width=&quot;814&quot; data-origin-height=&quot;1590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 영상을 출력해볼께요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.10.41.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;1412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZxfhX/btsMTIj9qLW/kcIKfC9KZZd2DCSgBzTit0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZxfhX/btsMTIj9qLW/kcIKfC9KZZd2DCSgBzTit0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZxfhX/btsMTIj9qLW/kcIKfC9KZZd2DCSgBzTit0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZxfhX%2FbtsMTIj9qLW%2FkcIKfC9KZZd2DCSgBzTit0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1236&quot; height=&quot;1412&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.10.41.png&quot; data-origin-width=&quot;1236&quot; data-origin-height=&quot;1412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 기다려 볼께요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.11.35.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X9e2a/btsMTilKmOd/mjC0tRgXrfp1jivOjqUpkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X9e2a/btsMTilKmOd/mjC0tRgXrfp1jivOjqUpkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X9e2a/btsMTilKmOd/mjC0tRgXrfp1jivOjqUpkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX9e2a%2FbtsMTilKmOd%2FmjC0tRgXrfp1jivOjqUpkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;540&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.11.35.png&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.20.38.png&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;1050&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ6fnV/btsMTie3qOD/YHqKZzVkKLu7mNW3qSwYYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ6fnV/btsMTie3qOD/YHqKZzVkKLu7mNW3qSwYYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ6fnV/btsMTie3qOD/YHqKZzVkKLu7mNW3qSwYYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ6fnV%2FbtsMTie3qOD%2FYHqKZzVkKLu7mNW3qSwYYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;571&quot; height=&quot;459&quot; data-filename=&quot;스크린샷 2025-03-12 오후 2.20.38.png&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;1050&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게해서 hey-gen 을 이용한 몇가지 방법으로 ai 아바타를 생성해보았는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 룩을 생성해주고, 간단한 설명만으로 아바타를 생성해주고, 내사진, 내얼굴로 아바타를 만들수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 디테일한 퀄리티가 아쉽습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주의&lt;br /&gt;&lt;/b&gt;*** 아바타 생성시 주의할점은 연예인 혹은 다른 누군가의 사진을 도용하여 아바타를 생성하면 안됩니다.&lt;br /&gt;*** 해외 배우 가수 등의 경우 아바타 생성시 필터링에 걸리는 경우가 있습니다. &lt;br /&gt;*** 악용되는 경우를 방지 하기 위한 조치로 보여집니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오후 3.04.24.png&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m7Jh8/btsMGPkR36s/YQFDW1YPSDxjjdQHqdIbG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m7Jh8/btsMGPkR36s/YQFDW1YPSDxjjdQHqdIbG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m7Jh8/btsMGPkR36s/YQFDW1YPSDxjjdQHqdIbG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm7Jh8%2FbtsMGPkR36s%2FYQFDW1YPSDxjjdQHqdIbG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;387&quot; height=&quot;367&quot; data-filename=&quot;스크린샷 2025-03-12 오후 3.04.24.png&quot; data-origin-width=&quot;387&quot; data-origin-height=&quot;367&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT 테크 기술 지식 공유/AI 정보 공유</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/193</guid>
      <comments>https://kimback03.tistory.com/193#entry193comment</comments>
      <pubDate>Wed, 12 Mar 2025 15:07:29 +0900</pubDate>
    </item>
    <item>
      <title>코딩강의 5화 - flutter dart 앱개발 리스트와 맵 with gpt</title>
      <link>https://kimback03.tistory.com/192</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault (1).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHRgFc/btsMTk4W7TM/xAKMGIvwlHDMRoY6oeH3Ck/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHRgFc/btsMTk4W7TM/xAKMGIvwlHDMRoY6oeH3Ck/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHRgFc/btsMTk4W7TM/xAKMGIvwlHDMRoY6oeH3Ck/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHRgFc%2FbtsMTk4W7TM%2FxAKMGIvwlHDMRoY6oeH3Ck%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault (1).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/To4Aj_2MdYY&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/To4Aj_2MdYY&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=To4Aj_2MdYY&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/g6p1n/hyYuctUPEb/htEzOOB72dMuixTgAOK59K/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=156_62_1198_234,https://scrap.kakaocdn.net/dn/cejAxr/hyYvi1j529/r9YfdxoUuJgYxKudJUPFB0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=156_62_1198_234&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;ai친구들과 함께 배우는 코딩교실 - 5화 리스트와 맵&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/To4Aj_2MdYY&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*아래의 글은 유튜브 코드와트 채널에서 연재되는 강의를 글로 작성한 내용입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*대화형식으로 진행되며, ai chat GPT를 활용한 코딩이 핵심입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*시대에 맞게 코딩.개발의 조력자로써 ai를 잘 활용것이 목표입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 코드와트 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 리스트와 맵에 대해서 배워보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩 개발하면서 &lt;b&gt;리스트&lt;/b&gt;와 &lt;b&gt;맵&lt;/b&gt;은 가장 많이 사용하기 때문에 꼭 알아두셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;리스트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 말그대로 목록형태로 데이터를 표시하기 위한 저장형태를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자료형(Data Type)이란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 저장을 위한 구조나 형태를 말합니다. 사실은 우리가 앞에서 쓰고 있던 int, double, String 또한 자료형의 한 종류였습니다. &lt;br /&gt;마찬가지로 리스트 또한 목록 형태의 데이터 저장을 위한 타입이구나. 생각하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목록이란 뭔가요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록을 만들기 위해서는 일정한 순서를 가지는 데이터가 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도서관에서 대여한 책 목록&lt;/li&gt;
&lt;li&gt;어제 과일가게에서 구매한 과일목록&lt;/li&gt;
&lt;li&gt;지난번 중간고사 성적목록&lt;/li&gt;
&lt;li&gt;이번달 카드결재 목록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등등 수없이 많은 데이터를 목록으로 만들수 있습니다. 어렵지 않죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 열거된 나열된 형식의 데이터들을 코드 에서는 리스트(List) 라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-10 오후 5.58.48.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/En3Wt/btsMGCdjq0y/80RpzJVs0tSMZXWDYKEpSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/En3Wt/btsMGCdjq0y/80RpzJVs0tSMZXWDYKEpSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/En3Wt/btsMGCdjq0y/80RpzJVs0tSMZXWDYKEpSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEn3Wt%2FbtsMGCdjq0y%2F80RpzJVs0tSMZXWDYKEpSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;992&quot; height=&quot;550&quot; data-filename=&quot;스크린샷 2025-03-10 오후 5.58.48.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 말그대로 목록 형태의 정보(데이터)를 담기위해 존재합니다. 긴말할것 없이 만들어보죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현실세계의 책장을 리스트로 만들어볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746328773&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookshelf = ['삼국지', '해리포터', '영웅문', '반지의제왕'];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 좋아하는 무협, 판타지 서적이 가득찬 책장이 완성되었습니다. 이 책장에는 순서가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽에서부터 0번 부터 시작해서 3번까지 번호가 매겨져 있죠. 보이지 않지만 이와 같은 번호를 &lt;b&gt;인덱스&lt;/b&gt; 라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 인덱스는 기본적으로 0부터 시작한다고 생각하면 됩니다. 그럼 이 책장에서 책을 꺼내볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746354471&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var book = bookshelf[0];
print(book); //삼국지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &quot;리스트가 목록이고&amp;hellip; 목록형태의 데이터를 활용하기 위해 쓴다? 그리고 꺼낼때는 인덱스를 활용한다??? 너무 헷갈려요.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헷갈리는게 당연합니다. gpt를 통해서 자세하게 알아볼께요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.26.46.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;1044&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgkTtB/btsMGqkVVYn/ayHOfSt3WmrDHHQak6UZo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgkTtB/btsMGqkVVYn/ayHOfSt3WmrDHHQak6UZo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgkTtB/btsMGqkVVYn/ayHOfSt3WmrDHHQak6UZo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgkTtB%2FbtsMGqkVVYn%2FayHOfSt3WmrDHHQak6UZo0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1614&quot; height=&quot;1044&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.26.46.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;1044&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.26.57.png&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uMs7m/btsMF6Acytz/6j5H45a2hXT4rQlMqkfOp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uMs7m/btsMF6Acytz/6j5H45a2hXT4rQlMqkfOp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uMs7m/btsMF6Acytz/6j5H45a2hXT4rQlMqkfOp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuMs7m%2FbtsMF6Acytz%2F6j5H45a2hXT4rQlMqkfOp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1704&quot; height=&quot;1030&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.26.57.png&quot; data-origin-width=&quot;1704&quot; data-origin-height=&quot;1030&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.07.png&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RGueO/btsMHnutjLH/IVEoYW5h3X4iRT6kfCSD60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RGueO/btsMHnutjLH/IVEoYW5h3X4iRT6kfCSD60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RGueO/btsMHnutjLH/IVEoYW5h3X4iRT6kfCSD60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRGueO%2FbtsMHnutjLH%2FIVEoYW5h3X4iRT6kfCSD60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1686&quot; height=&quot;1210&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.07.png&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;1210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.25.png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1046&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rMZTq/btsMIsVTkPC/fCKfAqa04OEzWVQC6o5OE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rMZTq/btsMIsVTkPC/fCKfAqa04OEzWVQC6o5OE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rMZTq/btsMIsVTkPC/fCKfAqa04OEzWVQC6o5OE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrMZTq%2FbtsMIsVTkPC%2FfCKfAqa04OEzWVQC6o5OE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1708&quot; height=&quot;1046&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.25.png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1046&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.38.png&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdLA6B/btsMHoNIqaa/FHshS1BZ0dXiuRom6r1sDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdLA6B/btsMHoNIqaa/FHshS1BZ0dXiuRom6r1sDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdLA6B/btsMHoNIqaa/FHshS1BZ0dXiuRom6r1sDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdLA6B%2FbtsMHoNIqaa%2FFHshS1BZ0dXiuRom6r1sDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1684&quot; height=&quot;1848&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.38.png&quot; data-origin-width=&quot;1684&quot; data-origin-height=&quot;1848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.50.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;996&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rbRAs/btsMH8QUlsB/zKkT7fjBWMLQZTxRaPH3u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rbRAs/btsMH8QUlsB/zKkT7fjBWMLQZTxRaPH3u0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rbRAs/btsMH8QUlsB/zKkT7fjBWMLQZTxRaPH3u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrbRAs%2FbtsMH8QUlsB%2FzKkT7fjBWMLQZTxRaPH3u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1676&quot; height=&quot;996&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.50.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;996&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.59.png&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;1190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1yvVk/btsMINZOJRu/UP6tGywk11xMDVQTJBevK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1yvVk/btsMINZOJRu/UP6tGywk11xMDVQTJBevK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1yvVk/btsMINZOJRu/UP6tGywk11xMDVQTJBevK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1yvVk%2FbtsMINZOJRu%2FUP6tGywk11xMDVQTJBevK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1662&quot; height=&quot;1190&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.27.59.png&quot; data-origin-width=&quot;1662&quot; data-origin-height=&quot;1190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 만든 리스트에 데이터를 추가 하거나, 빼거나 할때는 어떻게 해야할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에는 &lt;b&gt;편리한 함수&lt;/b&gt;들이 기본적으로 만들어져 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 추가하기, 삭제하기, 정렬하기 등의 편리한 함수를 그냥 쓰기만 하면 되지요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이제 실제로 코딩해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746500387&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookshelf = ['삼국지', '해리포터', '영웅문', '반지의제왕'];
var book = bookshelf[0];
print(book); //삼국지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 만든 코드에서 값을 추가 하고 (책) 삭제하는 예제를 만들어볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746514820&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookshelf = ['삼국지', '해리포터', '영웅문', '반지의제왕'];
var book = bookshelf[0];
print(book); //삼국지

bookshelf.add('호빗');
print(bookshelf); //&quot;삼국지&quot;, &quot;해리포터&quot;, &quot;영웅문&quot;, &quot;반지의제왕&quot;

bookshelf.remove('삼국지');
print(bookshelf); //&quot;해리포터&quot;, &quot;영웅문&quot;, &quot;반지의제왕&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가요? 우리가 배운 함수 사용법 기억나시죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변수에 . 을 찍고 함수 사용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 생성(실체를 만들고)하고 bookshelf 라는 변수에 담았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;담는 순간 bookshelf 변수의 타입은 리스트라는 타입으로 정해진 거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 리스트 형태의 변수인 bookshelf 를 통해서 . 을 찍고 add, remove 와 같은 함수를 사용할 수 있는것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 더 해볼께요. 이번에는 지난번 반복문 배울때 잠깐 배웠던 for in과 for each 라는 반복문을 사용해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록의 내용을 간단하게 순회 할때 사용하면 편리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746605221&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookshelf = ['삼국지', '해리포터', '영웅문', '반지의제왕'];
for(var book in bookshelf){
	print(book);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for문 작성보다 정말 간단하게 반복문을 작성할수가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복하면서 var book 이라는 변수에 책을 하나씩 담고 그걸 출력하는 예제에요. 하나더 해봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746620853&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookshelf = ['삼국지', '해리포터', '영웅문', '반지의제왕'];
bookshelf.forEach((book){
	print(book);	
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 forEach 라고 하는 함수를 사용했는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구문이 조금 특이합니다만 우선 이렇게 사용할수도 있구나 하고 알아둡시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에서 더 자세히 알아볼께요.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;맵(딕셔너리)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 일상에서 모르는 단어가 나오면 뜻을 찾아보기 위해 검색을 하고 사전을 찾아봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩이라는 단어를 찾아볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.31.12.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7KjVU/btsMIt1yxjr/NkxDhf50MLYa6cYUMMfE6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7KjVU/btsMIt1yxjr/NkxDhf50MLYa6cYUMMfE6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7KjVU/btsMIt1yxjr/NkxDhf50MLYa6cYUMMfE6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7KjVU%2FbtsMIt1yxjr%2FNkxDhf50MLYa6cYUMMfE6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1656&quot; height=&quot;1412&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.31.12.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩이라는 키워드로 &amp;rarr; 뜻이라는 데이터를 찾았습니다. 하나더 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.31.33.png&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;1284&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPaGO5/btsMHg3itpx/WozA1w8ICuiJIkHYB5rw5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPaGO5/btsMHg3itpx/WozA1w8ICuiJIkHYB5rw5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPaGO5/btsMHg3itpx/WozA1w8ICuiJIkHYB5rw5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPaGO5%2FbtsMHg3itpx%2FWozA1w8ICuiJIkHYB5rw5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1646&quot; height=&quot;1284&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.31.33.png&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;1284&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Develop 이라는 단어(키워드)로 성장하다, 개발하다 등의 데이터를 찾아냈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 코딩에서 키(key)와 값(value)이라는 개념으로 연결되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;coding이라는 키 값과 value 라는 데이터(우리가 찾는 정보) 를 연결해주는것이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 방식의 데이터 저장형식을 맵이라고 불러요. Map은 지도의 의미 보다는 Mapping 즉 값과 값의 연결의 의미로 알고있으면 되요. 또는 다른 언어에서는 딕셔너리, 사전 그 자체의 의미로 불리웁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♂️ &quot;잘 모르겠는데요&amp;hellip; 키 밸류? &amp;hellip;. 맵이요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot; data-token-index=&quot;0&quot;&gt;설명만으로는 이해가 어려운게 당연하구요. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot; data-token-index=&quot;0&quot;&gt;gpt 조교를 통해 자세하게 알아볼께요.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.32.33.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c00fR0/btsMIceAZRe/OajKeNpMDP046ntZaihLik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c00fR0/btsMIceAZRe/OajKeNpMDP046ntZaihLik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c00fR0/btsMIceAZRe/OajKeNpMDP046ntZaihLik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc00fR0%2FbtsMIceAZRe%2FOajKeNpMDP046ntZaihLik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;364&quot; height=&quot;104&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.32.33.png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.32.47.png&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;688&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZEWnA/btsMHR9OLif/YxJysOEfe6quM2rPxm9jNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZEWnA/btsMHR9OLif/YxJysOEfe6quM2rPxm9jNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZEWnA/btsMHR9OLif/YxJysOEfe6quM2rPxm9jNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZEWnA%2FbtsMHR9OLif%2FYxJysOEfe6quM2rPxm9jNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1594&quot; height=&quot;688&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.32.47.png&quot; data-origin-width=&quot;1594&quot; data-origin-height=&quot;688&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.01.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;1282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eBgPt2/btsMHveKoOM/T1lQezMqz8ltStI4GxyLk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eBgPt2/btsMHveKoOM/T1lQezMqz8ltStI4GxyLk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eBgPt2/btsMHveKoOM/T1lQezMqz8ltStI4GxyLk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeBgPt2%2FbtsMHveKoOM%2FT1lQezMqz8ltStI4GxyLk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1618&quot; height=&quot;1282&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.01.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;1282&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.14.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VF2WQ/btsMH4A7bsH/EwOZKKPsu1J4B5RKXqAeVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VF2WQ/btsMH4A7bsH/EwOZKKPsu1J4B5RKXqAeVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VF2WQ/btsMH4A7bsH/EwOZKKPsu1J4B5RKXqAeVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVF2WQ%2FbtsMH4A7bsH%2FEwOZKKPsu1J4B5RKXqAeVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1656&quot; height=&quot;1148&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.14.png&quot; data-origin-width=&quot;1656&quot; data-origin-height=&quot;1148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.29.png&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ0Zfo/btsMHdlegot/T17RM5tad0mAmZkaeLOzZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ0Zfo/btsMHdlegot/T17RM5tad0mAmZkaeLOzZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ0Zfo/btsMHdlegot/T17RM5tad0mAmZkaeLOzZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ0Zfo%2FbtsMHdlegot%2FT17RM5tad0mAmZkaeLOzZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1682&quot; height=&quot;984&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.29.png&quot; data-origin-width=&quot;1682&quot; data-origin-height=&quot;984&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.41.png&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;1038&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVRlXk/btsMFJd7QxI/OLLKrOwFZElkeYrIWSUZu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVRlXk/btsMFJd7QxI/OLLKrOwFZElkeYrIWSUZu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVRlXk/btsMFJd7QxI/OLLKrOwFZElkeYrIWSUZu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVRlXk%2FbtsMFJd7QxI%2FOLLKrOwFZElkeYrIWSUZu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1650&quot; height=&quot;1038&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.41.png&quot; data-origin-width=&quot;1650&quot; data-origin-height=&quot;1038&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.53.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;1484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JSeRL/btsMHwSeST9/oMP4ijOy1HWktlZDFMBnyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JSeRL/btsMHwSeST9/oMP4ijOy1HWktlZDFMBnyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JSeRL/btsMHwSeST9/oMP4ijOy1HWktlZDFMBnyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJSeRL%2FbtsMHwSeST9%2FoMP4ijOy1HWktlZDFMBnyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1676&quot; height=&quot;1484&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.33.53.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;1484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.34.04.png&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zJjVW/btsMIaVtriX/PGjvf4dW2Sc7HkaKvZeofK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zJjVW/btsMIaVtriX/PGjvf4dW2Sc7HkaKvZeofK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zJjVW/btsMIaVtriX/PGjvf4dW2Sc7HkaKvZeofK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzJjVW%2FbtsMIaVtriX%2FPGjvf4dW2Sc7HkaKvZeofK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1690&quot; height=&quot;786&quot; data-filename=&quot;스크린샷 2025-03-12 오전 11.34.04.png&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 실행해봅시다. 책에 대한 정보가 담긴 사전을 만들어본다고 생각할께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책이름을 키워드로 넣으면 그 책에 대한 정보를 가져올수 있도록 하는거죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746862803&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookInfo = {
'해리포터':'해리포터는...', 
'반지의제왕':'반지의제왕은...', 
'호빗':'호빗은...', 
'삼국지': '삼국지는...',
}

print(bookInfo['해리포터']);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 책정보가 담긴 맵이라는 데이터구조에서 내가 원하는 키워드 즉 해리포터라는 키워드로 그 책에 해당하는 정보를 가져왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{ 중괄호 열고 닫고 } 로 만들면 되고 그안에 데이터는 key : value 라는 쌍으로 이루어져 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이부분이죠.&lt;/p&gt;
&lt;pre id=&quot;code_1741746891704&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'해리포터':'해리포터는...'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &quot;쌤 &amp;lsquo; &amp;rsquo; 은 우리 문자를 사용할때 썼던거 같은데 이것도 문자인가요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나이스! 아주 좋은 질문입니다. 문자 형태가 맞아요. &amp;lsquo;해리포터&amp;rsquo; 라는 키워드도 문자이고 &amp;lsquo;내용&amp;rsquo; 또한 문자가 맞아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 숫자로도 할수 있나요? 맞아요. 숫자또한 키와 밸류로 사용할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책이름의 키워드로 해당 책이 몇권 팔렸는지 확인 해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741746991037&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main(){
  var bookSell = {
    '해리포터': 10,
    '반지의제왕' : 20,
    '삼국지' : 30
  };
  
  print(bookSell['삼국지']);

  var totalSum = bookSell['삼국지']! + bookSell['반지의제왕']! + bookSell['해리포터']!;
  print(totalSum);
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 숫자로 값을 표현할수 있고 숫자데이터를 합계를 내서 출력해봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 키를 숫자로 할수 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741747006285&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookInfoMap = {
    101: '해리포터',
    102: '반지의제왕',
    103: '삼국지'
  };
print(bookInfoMap[101]);
print(bookInfoMap[102]);
print(bookInfoMap[103]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 책 넘버가 있고 그 넘버로 책을 찾고 싶다고 했을때 이런식으로 대입하면 되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 추가하거나 삭제하는것도 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741747022686&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var bookInfoMap = {
    101: '해리포터',
    102: '반지의제왕',
    103: '삼국지'
  };

bookInfoMap[104] = '호빗'
print(bookInfoMap);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bookInfoMap이라는 변수명으로 [ 괄호 열고 새로운 키값 괄호닫고 ] = &amp;lsquo;호빗&amp;rsquo;이라는 새로운 밸류 즉 데이터를 대입해주면 됩니다. 어렵지 않죠? 101 의 데이터를 변경하고 싶다면? 그것도 쉽습니다. 그냥 새로운 데이터를 대입하면 되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741747041639&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bookInfoMap[101] = '영웅문'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제도 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제하는 함수인 remove 를 사용해서 지우고 싶은 키값을 넣어주기만 하면 끝이죠.&lt;/p&gt;
&lt;pre id=&quot;code_1741747057936&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;user.remove(101);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가요? 생각보다 쉽게 리스트와 맵에 대해서 배워보았는데요. 코딩하면서 필수적으로 쓰이는 자료형이니 꼭 익혀두시는게 좋겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Mobile Dev/코드와트 코딩강의</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/192</guid>
      <comments>https://kimback03.tistory.com/192#entry192comment</comments>
      <pubDate>Wed, 12 Mar 2025 11:40:14 +0900</pubDate>
    </item>
    <item>
      <title>코딩강의4 - flutter dart 클래스와 함수 그리고 생성자 with gpt</title>
      <link>https://kimback03.tistory.com/191</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baQBnc/btsMSu1GZBU/9kQXQuDycF7I6P96i3nXbk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baQBnc/btsMSu1GZBU/9kQXQuDycF7I6P96i3nXbk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baQBnc/btsMSu1GZBU/9kQXQuDycF7I6P96i3nXbk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaQBnc%2FbtsMSu1GZBU%2F9kQXQuDycF7I6P96i3nXbk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault.jpg&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/xJ47tK1t5Hg&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/xJ47tK1t5Hg&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=xJ47tK1t5Hg&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/TmOno/hyYqPRX5aN/eGBKZCwkWdwFVKXkQZAkA1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=92_420_422_516,https://scrap.kakaocdn.net/dn/co9m6P/hyYnbvwb4s/gtPa8g6vIf6ek7V72dkA1k/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=92_420_422_516&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;4화 클래스와 함수 그리고 생성자 (dart flutter 배우기)&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/xJ47tK1t5Hg&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*아래의 글은 유튜브 코드와트 채널에서 연재되는 강의를 글로 작성한 내용입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*대화형식으로 진행되며, ai chat GPT를 활용한 코딩이 핵심입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*시대에 맞게 코딩.개발의 조력자로써 ai를 잘 활용것이 목표입니다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 코드와트 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시간에는 제어문중에서 조건문과 반복문에 대해서 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.blueswt.com/190&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.03.02 - [개발 코딩 정보 공유/코드와트 코딩강의] - 코딩강의3 - dart / flutter 기초 제어문 조건문과 반복문&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 조건문을 통해서 필요한 조건에 만족할때만 실행하는 구문을 작성해봤구요. &lt;br /&gt;조건이 많을때는 else if 와 else 도 사용하는 방법을 배웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for 문이라고 하는 반복문을 통해서 효율적으로 코드를 작성하는 예제도 구현해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 내용은 이해하기 어려울수도 있지만,&amp;nbsp;집중해서 하나하나 천천히 같이 배워볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;클래스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 대부분의 언어들은 클래스라는 개념을 포함하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이상한 단어가 등장해서 당황하셨을거라 생각이 드네요. 우선 class의 뜻을 한번 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.15.35.png&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;737&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nhpXE/btsMB52yw0w/tWVHnhyNCAbYes4SRN6iK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nhpXE/btsMB52yw0w/tWVHnhyNCAbYes4SRN6iK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nhpXE/btsMB52yw0w/tWVHnhyNCAbYes4SRN6iK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnhpXE%2FbtsMB52yw0w%2FtWVHnhyNCAbYes4SRN6iK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;843&quot; height=&quot;737&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.15.35.png&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;737&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 알고있는 학급, 수업이라는 뜻 보다는 코딩에서의 클래스는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종류, 부류와 같이 비슷한 특성을 지닌 대상을 묶는 역할이라고 생각해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &quot;쌤&amp;hellip; 도대체 무슨 말인지 모르겠어요. 클래스?? 종류?? ...왓?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제나 그랫듯이 일단 class 를 만들고 실행해볼께요.&lt;/p&gt;
&lt;pre id=&quot;code_1741310296496&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
	var name = '홍길동';
	var age = '16';	
	var englishScore = '90';
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 클래스 입니다. class 라는 키워드로 시작해서 Student 라는 이름을 만들어 주고 중괄호 { 열고 } 닫고 하면 끝.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 나머지는 우리가 알고있던 변수 선언하는 방법이에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생이라는 큰 틀안에 이 학생의 이름과 나이, 성적등의 데이터를 작성할수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나더 해볼까요.&lt;/p&gt;
&lt;pre id=&quot;code_1741310413078&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;class Car {
	var name = '김과자의자동차';
	var oil = 'gasoline';
	var speed = '200km';
  	var brand  = '포르쉐';
 	var type = '슈퍼카';
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 포르쉐. 상상속 자동차죠?  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Car 라는 class 를 만들어 관련 데이터를 정의 해줄수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 함으로써 car에 대한 정보를 묶어서 그룹화하여 관리할수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 이클래스가 왜 필요하고 언제 어떻게 쓰는지 살펴볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741310486009&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
	var name = '홍길동';
	var age = '16';	
	var englishScore = '90';
	
}

class Car {
	var name = '김과자의자동차';
	var oil = 'gasoline';
	var speed = '200km';
  	var brand  = '포르쉐';
  	var type = '슈퍼카';
}

void main(){
	var student = Student();
	var car = Car();
	print(student.name);
	print(car.name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아까 만든 student class와 car class 를 작성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main 함수에 보시면 var student 는 우리가 알고 있는 변수 선언입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 정수나 문자를 대입했던 기억이 있죠? 하지만 이번에는 이상한 걸 대입하고 있네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 작성한 Student 라는 클래스 명을 작성하고 ( 괄호 열고 )괄호 닫는 이상한 코드가 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드가 바로 클래스(Class)를 객체화(Object) 시키는 코드인데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 우리가 열심히 작성한 클래스 라는것은 하나의 틀에 불과합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♂️ &quot;왓??????&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &quot;쌤&amp;hellip; 머리가 빙빙 도는데요? 설계도면인 클래스와 그걸가지고 만드는 실체가 객체라는건가요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞습니다. GPT 조교한테 설명을 부탁해보죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.26.49.png&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4qxQn/btsMC3izMrR/7HOBwgoUXQFk9ELcuHTAHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4qxQn/btsMC3izMrR/7HOBwgoUXQFk9ELcuHTAHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4qxQn/btsMC3izMrR/7HOBwgoUXQFk9ELcuHTAHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4qxQn%2FbtsMC3izMrR%2F7HOBwgoUXQFk9ELcuHTAHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;894&quot; height=&quot;170&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.26.49.png&quot; data-origin-width=&quot;894&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.08.png&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;505&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pWsEW/btsMEddseUy/MOBEhkyu2GgpW01kZIz7D1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pWsEW/btsMEddseUy/MOBEhkyu2GgpW01kZIz7D1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pWsEW/btsMEddseUy/MOBEhkyu2GgpW01kZIz7D1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpWsEW%2FbtsMEddseUy%2FMOBEhkyu2GgpW01kZIz7D1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;505&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.08.png&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.23.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;255&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmpNeJ/btsMCxdrUed/V82m0JHViWegg6Zh5EMNIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmpNeJ/btsMCxdrUed/V82m0JHViWegg6Zh5EMNIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmpNeJ/btsMCxdrUed/V82m0JHViWegg6Zh5EMNIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmpNeJ%2FbtsMCxdrUed%2FV82m0JHViWegg6Zh5EMNIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;867&quot; height=&quot;255&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.23.png&quot; data-origin-width=&quot;867&quot; data-origin-height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.36.png&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MXxL4/btsMC2qFnMk/PgLTBrz3AZSNtiKJWX6pw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MXxL4/btsMC2qFnMk/PgLTBrz3AZSNtiKJWX6pw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MXxL4/btsMC2qFnMk/PgLTBrz3AZSNtiKJWX6pw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMXxL4%2FbtsMC2qFnMk%2FPgLTBrz3AZSNtiKJWX6pw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;895&quot; height=&quot;723&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.36.png&quot; data-origin-width=&quot;895&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.48.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdVsfs/btsMElWCRg9/aMSpC61lddkuujdtsJu8Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdVsfs/btsMElWCRg9/aMSpC61lddkuujdtsJu8Wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdVsfs/btsMElWCRg9/aMSpC61lddkuujdtsJu8Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdVsfs%2FbtsMElWCRg9%2FaMSpC61lddkuujdtsJu8Wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;914&quot; height=&quot;676&quot; data-filename=&quot;스크린샷 2025-03-07 오전 10.27.48.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;class 그 자체로는 그냥 껍데기일 뿐이죠. 아무런일도 할수없어요.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;물건을 만들기 위한 설계도면 이라고 생각하시면 되겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;설계도면으로 대상을 실체화 하기 위해서 바로 위에서 작성한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Student();&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라는 코드가 필요한거에요.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이해를 돕기위해 붕어빵과 붕어빵틀을 비유해보겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붕어빵이 만들어지기 위해서는 붕어빵틀이 필요합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붕어빵이 실체 = 객체(object) 이고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붕어빵틀 = class 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;class 만으로는 아무일도 일어나지 않습니다. class로 실체를 만들어야 사용할수가 있는것이죠.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;붕어빵을 만들어내는 코드가 바로 Student(); 라는 코드 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 코드는 특수한 함수로써 생성자라고 불리웁니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;함수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 특정한 기능을 수행할때 작성하는 구문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 우리가 계속 써왔지만 인지하지 못했던 main이라고 하는 함수가 있었는데요.&lt;/p&gt;
&lt;pre id=&quot;code_1741311034724&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main(){
	print('메인함수');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이코드 입니다. 우리는 이미 함수를 사용하고 있었어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;main 함수는 특수한 함수로써 모든 프로그램의 진입점으로 쓰이는 함수 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말해 시작(Run) 하면 우선적으로 main으로 진입한다고 생각하시면 되요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♂️ &quot;아 그래서 저희가 실행버튼을 누르면 main 안에 작성한 코드가 실행되는거였군요.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 main함수 말고 우리도 함수를 만들수 있지 않을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맞아요. 함수를 한번 만들어 볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741311185102&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void study(){
	print(name + '가 공부를 시작합니다.');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에 있는 void 라는 키워드는 아무것도 리턴하지 않는다는 표시이고, 생략도 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 매개변수 또는 파라미터 라고 하는 값을 전달 받고, 특정 값을 리턴할수 있는 특수한 친구 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 들어오는 input 이 있고 나가는 output이 확실한 친구이죠. 한번 해볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741311231125&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
	var name = '홍길동';
	var age = '16';	
	var englishScore = '90';
  
  
  //1번
  void study(){
    print(name + '가 공부를 합니다.');
  }
  
  //2번
  String returnScore(){
    return englishScore;
  }
  
  //3번
  int intOutScore(int addScore){
    return addScore + englishScore;
  }
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 살펴볼께요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이부분은 쉽습니다. void는 리턴이 없다는 뜻이고, study 라는 이름의 함수이며, print문을 실행한다는 뜻입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1741311304770&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void study(){
	print(name + '가 공부를 합니다.');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째는 리턴값이 있어요. void 가 아니라 우리가 앞에서 배웟던 문자형 타입 = String 이 들어가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;점수 계산을 마치고 (특정기능) 영어 점수를 반환 하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1741311370054&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//2번
String returnScore(){ //실행할걸 다 하고 String 형태의 리턴을 하겠다는 말이다.
  	print('점수계산중...');
	return englishScore;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 한발더 나아가서 파라미터 매개변수 를 전달받는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1741311432659&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//3번
int intOutScore(int result){
	return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨앞에 int 라고 된걸 보니, 아까와 다르게 정수 형태의 반환값을 전달한다는 말이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;intOutScore라는 이름을 사용했고, 괄호 안에 int result 이부분이 바로 매개변수 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 이 함수를 사용할때 같이 전달해줘야하는 값이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할때는 아래와 같이 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741311507990&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main(){
	var student = Student();
	var score = student.inOutScore(80);
	print(score);
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 하나씩 살펴보시죠.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;메인함수에서 Student 의 실체 = 붕어빵을 만들고&lt;/li&gt;
&lt;li&gt;student 변수를 사용해서 . 을 찍고 inOutScore 라고 작성한뒤 괄호 열고 그안에 아까 우리가 작성한 파라미터로 전달할 값을 주면 됩니다. 위에서 int 타입이라고 했으니 int를 전달해야 겠죠?&lt;/li&gt;
&lt;li&gt;var score 변수를 선언한 이유는 student.inOutScore(80); 가 int를 리턴하기 때문입니다.&lt;/li&gt;
&lt;li&gt;print 문을 통해 리턴받은 값을 출력합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &amp;ldquo;Student(); 는 우리가 class를 만들면서 정의하지 않았는데&amp;hellip;어디서 나온거죠?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;생성자&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 함수는 특수한 용도입니다. 우리가 만들지 않아도 기본적으로 가지고 있으며, 사용할수 있습니다. 이 특수한 함수의 용도는 우리가 해당 클래스를 말그대로 생성할 시점에 사용하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 클래스를 만들때 이렇게 만들었죠? Student 라는 클래스 명을 작성했었죠.&lt;/p&gt;
&lt;pre id=&quot;code_1741311637622&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Student {
	var name = '홍길동';
	var age = '16';	
	var englishScore = '90';
  
  }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1741311612422&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var student = Student();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자는 해당하는 클래스명을 따라갑니다. 그래서 Student()와 같이 생성자를 사용하는것이죠. 다양하게 생성자를 만드는 방법이 있지만 우선은 기본형의 생성자에 익숙해지도록 합시다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스, 함수, 생성자 등에 대해서 배웠으니 이제 자유롭게 내 아이디어를 구현하는 단계입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순하게 아까 했던예제를 통해서 확장해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741311774042&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class MyCar {
	var name = '김과자의자동차';
	var oil = 'gasoline';
	var speed = '200km';
  	var brand  = '포르쉐';
  	var type = '슈퍼카';
  
  void drive(){
	  print('자동차가 달립니다.');
  }
  
  void stop(){
	  print('자동차가 정지합니다.');
  }
  
  void turnOnLight(){
	  print('라이트를 조작합니다.');
  }
}

void main(){
	var	myCar =	MyCar();
	myCar.drive();
	myCar.stop();
	myCar.turnOnLight();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가요? 나만의 훌륭한 자동차를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 문자를 출력하는 예제이지만 클래스, 함수, 변수선언과 같은 필수적인 구문이 모두 들어가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제를 참고해서 자신만의 아이디어를 실제로 구현해보세요. 물론 아직은 콘솔창에 출력하는것이 전부이지만요.&lt;/p&gt;</description>
      <category>Mobile Dev/코드와트 코딩강의</category>
      <category>IOS</category>
      <category>개발</category>
      <category>다트</category>
      <category>안드로이드</category>
      <category>앱개발</category>
      <category>코딩</category>
      <category>코딩배우기</category>
      <category>플러터</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/191</guid>
      <comments>https://kimback03.tistory.com/191#entry191comment</comments>
      <pubDate>Fri, 7 Mar 2025 10:45:14 +0900</pubDate>
    </item>
    <item>
      <title>코딩강의3 - flutter dart 앱개발 기초 제어문 조건문과 반복문 with gpt</title>
      <link>https://kimback03.tistory.com/190</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;maxresdefault (2).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxNS2A/btsMTclBucu/dEzpjmjGYcZjR7zEOuMClk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxNS2A/btsMTclBucu/dEzpjmjGYcZjR7zEOuMClk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxNS2A/btsMTclBucu/dEzpjmjGYcZjR7zEOuMClk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxNS2A%2FbtsMTclBucu%2FdEzpjmjGYcZjR7zEOuMClk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;720&quot; data-filename=&quot;maxresdefault (2).webp&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;a href=&quot;https://youtu.be/RAO9WOqfIWQ?si=-rD8drbgYk3WYljE&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/RAO9WOqfIWQ?si=-rD8drbgYk3WYljE&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=RAO9WOqfIWQ&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/cC58WW/hyYrRDZi18/GUe4eKUP6vlFDa5pVqFss1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=64_434_982_550,https://scrap.kakaocdn.net/dn/bMoQPq/hyYugXnsKK/lS9xExakygDRfRW2TFY5u1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=64_434_982_550&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;ai친구들과 함께 배우는 - 3화 앱개발 배우기 조건문과 반복문 (dart/f&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/RAO9WOqfIWQ&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*아래의 글은 유튜브 코드와트 채널에서 연재되는 강의를 글로 작성한 내용입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*대화형식으로 진행되며, ai chat GPT를 활용한 코딩이 핵심입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*시대에 맞게 코딩.개발의 조력자로써 ai를 잘 활용것이 목표입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;안녕하세요. 지난시간에 이어서 오늘은 개발하는데 있어서 필수적인 제어문이죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;조건문과 반복문을 배워볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;조건문&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우선 조건문이에요. 우리 영어를 배울때 if 가 무슨뜻인지는 다들 알고 있잖아요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.50.46.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wEtkK/btsMzhVA98V/y6Z024wtVK9TbEQkEoTwS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wEtkK/btsMzhVA98V/y6Z024wtVK9TbEQkEoTwS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wEtkK/btsMzhVA98V/y6Z024wtVK9TbEQkEoTwS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwEtkK%2FbtsMzhVA98V%2Fy6Z024wtVK9TbEQkEoTwS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1422&quot; height=&quot;1000&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.50.46.png&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;영어문장을 만들듯이 코딩할때에도 if 를 쓰는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;어디에 쓰는지 한번 알아볼께요. 우리가 해왔던것 처럼 아래의 코드를 그냥 무작정 실행해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740905499441&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name = '김과자';
if(name == '김과자'){
	print('안녕하세요' + name + '입니다.');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;정확히는 모르지만 console 창에 실행이 잘 됐죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이와 같이 if 를 통해 작성된 블록을 if문 이라고 부르고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이와 같이 특정 조건에 의해서 실행을 제어하는 구문을 조건문 이라고 불러요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;i&gt; &amp;zwj;♀️&quot;if&amp;hellip; 제어문 &amp;hellip;조건문&amp;hellip;. 왔&amp;hellip;.?? 너무 어려워요.&quot;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아마도 생소한 단어들 때문에 어려움을 느끼겠지만 저런 구문을 만나면 이렇게 해석해보세요.&lt;/p&gt;
&lt;pre id=&quot;code_1740905598395&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if(name == '김과자')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1740906819475&quot; class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;if([조건]){
	[실행코드]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;if 를 &lt;b&gt;&quot;만약에&amp;hellip; 이랬다면&quot;&lt;/b&gt; 이라고 말하면서 따라가 볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 만약에 name 라는 변수에 들어있는 값이 &amp;lsquo;김과자&amp;rsquo; 라는 문자라면 그렇다면 (조건이 성립하면)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. { 로 시작하는 블록안에 진입하게 되요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 그리고 그 안에 있는 코드가 실행되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4. 우리는 간단하게 print 를 통해서 name 이 출력하는 예제를 작성했어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;5. 그리고 나서 반드시 } 다시 닫아주셔야 되요. 항상 문단속을 잘 해야합니다. 열었으면 닫자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt; &amp;zwj;♂️ &quot;아 그럼 만약에 여러가지 조건이 많다면 어떻게 하면 되나요?&quot;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;if 와 함께 쓸수 있는 else와 else if 를 통해서 상황에 맞는 명확한 제어 블록을 만들수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;if문 말고도 switch case 라고 하는 문법이 있는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이건 더 뒤에서 배워볼께요. 우선은 if 문만으로 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;gpt 통해서 더 자세히 알아볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.07.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DKuBk/btsMzuHfZl1/4gjWzNyZKafXkaK3aZbKi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DKuBk/btsMzuHfZl1/4gjWzNyZKafXkaK3aZbKi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DKuBk/btsMzuHfZl1/4gjWzNyZKafXkaK3aZbKi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDKuBk%2FbtsMzuHfZl1%2F4gjWzNyZKafXkaK3aZbKi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1442&quot; height=&quot;706&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.07.png&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.17.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;1178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFiTxK/btsMx6tIWWc/owBAgf9deKgW2o30Hw9f0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFiTxK/btsMx6tIWWc/owBAgf9deKgW2o30Hw9f0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFiTxK/btsMx6tIWWc/owBAgf9deKgW2o30Hw9f0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFiTxK%2FbtsMx6tIWWc%2FowBAgf9deKgW2o30Hw9f0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1566&quot; height=&quot;1178&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.17.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.28.png&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Y1uQP/btsMAWvYpCr/5TmGuGbKOAXWgFTceMZR7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Y1uQP/btsMAWvYpCr/5TmGuGbKOAXWgFTceMZR7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Y1uQP/btsMAWvYpCr/5TmGuGbKOAXWgFTceMZR7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FY1uQP%2FbtsMAWvYpCr%2F5TmGuGbKOAXWgFTceMZR7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1542&quot; height=&quot;998&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.28.png&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;998&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.36.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;1152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSQs5K/btsMzd6Ftx3/IWvx5BThUt0RhDHi16qChk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSQs5K/btsMzd6Ftx3/IWvx5BThUt0RhDHi16qChk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSQs5K/btsMzd6Ftx3/IWvx5BThUt0RhDHi16qChk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSQs5K%2FbtsMzd6Ftx3%2FIWvx5BThUt0RhDHi16qChk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1532&quot; height=&quot;1152&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.36.png&quot; data-origin-width=&quot;1532&quot; data-origin-height=&quot;1152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.46.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;1260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SUMUI/btsMAt8M0ZJ/p426Q5rGKFnsoIkKevr3Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SUMUI/btsMAt8M0ZJ/p426Q5rGKFnsoIkKevr3Lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SUMUI/btsMAt8M0ZJ/p426Q5rGKFnsoIkKevr3Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSUMUI%2FbtsMAt8M0ZJ%2Fp426Q5rGKFnsoIkKevr3Lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1530&quot; height=&quot;1260&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.56.46.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;1260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;switch case 문은 참고만 해두도록 하세요. 우선은 if문을 중심으로 예제를 실행할거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;자 그럼 배운 내용을 바탕으로 코드를 작성 해볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740905828120&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name = '김과자';
if(name == '김과자'){

}else if(name == '홍과자'){

}else if(name == '이과자'){

}else if(name == '박과자'){

} //else if는 계속 붙혀도 괜찮다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;추가 적인 조건이 많다면 else if 를 통해서 세부적인 조건문을 작성하면 됩니다. 원하는대로 계속 추가해주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1740905848161&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name = '김과자';
if(name == '김과자'){

}else if(name == '홍과자'){

}else if(name == '이과자'){

}else if(name == '박과자'){

}else{ //그 외에 모든것들 처리하고 싶다면...
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그 이외에 너무 많아서 일일이 작성하기 힘들다면 else 구문을 통해서 처리하면 되니까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;상황에 맞게 잘 응용하는것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;또한 복잡한 조건을 만들어야 한다면 아래와 같이 중첩 되는 형태의 블록을 만들수도 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740905868584&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name = '김과자';
var age = 20;
if(name == '김과자'){
    if(age == 20){ //if 블록안에 또 다른 if블록을 작성할수 있다. 

    }else if(age == 30){ //else if 도 당연히 가능!

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하지만 과도하게 블록을 중첩시키면 나중에 코드를 읽을때 분석하기가 굉장히 어려워진다는 점! 기억해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;일명 조건문 지옥에 빠질수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740905908029&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if([조건]){
	if([조건]){
		if([조건]){
			if([조건]){
				if([조건]){
		
				}else if([조건]){
				
				}
			}else if([조건]){
			
			}
		}else if([조건]){
		
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;보기만 해도 눈이 아프죠? 이런 스타일의 코드 작성은 지양하는것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.58.56.png&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wdsZn/btsMArQG6PN/QfNaVNPO5BE5IY1PrZESnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wdsZn/btsMArQG6PN/QfNaVNPO5BE5IY1PrZESnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wdsZn/btsMArQG6PN/QfNaVNPO5BE5IY1PrZESnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwdsZn%2FbtsMArQG6PN%2FQfNaVNPO5BE5IY1PrZESnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;322&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.58.56.png&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;반복문&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이번에는 for 라는 구문을 이용해서 반복문이라고 하는 블록을 만들어 볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;반복문은 말 그대로 무언가를 반복해서 원하는 동작을 작성하는 문법이에요. 한번 해볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우선 가장 기본적인 형태의 for문이에요. 일단 실행해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740905988645&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for(var i=0; i &amp;lt; 10 ;i++){
   print(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.59.55.png&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UqS8F/btsMzQ4qUhA/9EQ6YK8qVsLhTUNPl7A6m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UqS8F/btsMzQ4qUhA/9EQ6YK8qVsLhTUNPl7A6m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UqS8F/btsMzQ4qUhA/9EQ6YK8qVsLhTUNPl7A6m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUqS8F%2FbtsMzQ4qUhA%2F9EQ6YK8qVsLhTUNPl7A6m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;226&quot; height=&quot;428&quot; data-filename=&quot;스크린샷 2025-03-02 오후 5.59.55.png&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;어때요? 잘 모르겠지만 console 창에 순서대로 무언가 출력되고 있죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이번에는 for을 쓰지 않고 console 창에 똑같이 표시하려면 어떻게 할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740906018077&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(0);
print(1);
print(2);
print(3);
print(4);
print(5);
print(6);
print(7);
print(8);
print(9);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이런식으로 코딩하겠죠? 이런 방식의 코딩은 굉장히 비효율 적이겠죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 반복을 통한 동작을 코드로 작성해야 할때는 for문 을 쓰면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt; &amp;zwj;♂️ &quot;쌤 그냥 for문&amp;hellip; 어려운데 안쓰고 반복되는 코드를 다 작성하면 되지 않을까요?&quot;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;반복되는 횟수가 적을때는 그렇게 해도 되지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 100번 1000번 반복해야한다면 어떻게 해야 할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇기 때문에 for문을 통해 효율적으로 코드를 작성하는게 좋겠죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;gpt를 통해서 좀더 자세하게 알아볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.03.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bU8NmQ/btsMx9cRW7W/4WkZe82fv845Lh68VkZIAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bU8NmQ/btsMx9cRW7W/4WkZe82fv845Lh68VkZIAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bU8NmQ/btsMx9cRW7W/4WkZe82fv845Lh68VkZIAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbU8NmQ%2FbtsMx9cRW7W%2F4WkZe82fv845Lh68VkZIAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1464&quot; height=&quot;478&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.03.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.13.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;1230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFmcZr/btsMytCcgYw/pbdzwmjndrkUZf5FHXATP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFmcZr/btsMytCcgYw/pbdzwmjndrkUZf5FHXATP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFmcZr/btsMytCcgYw/pbdzwmjndrkUZf5FHXATP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFmcZr%2FbtsMytCcgYw%2FpbdzwmjndrkUZf5FHXATP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1450&quot; height=&quot;1230&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.13.png&quot; data-origin-width=&quot;1450&quot; data-origin-height=&quot;1230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.23.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yuFtA/btsMx8kMTfO/5Ve4pjOJejandrIluSsW4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yuFtA/btsMx8kMTfO/5Ve4pjOJejandrIluSsW4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yuFtA/btsMx8kMTfO/5Ve4pjOJejandrIluSsW4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyuFtA%2FbtsMx8kMTfO%2F5Ve4pjOJejandrIluSsW4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1430&quot; height=&quot;492&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.23.png&quot; data-origin-width=&quot;1430&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.37.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mZCOm/btsMAY8p3x8/JARCWZYvSr2nx62bvOqoxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mZCOm/btsMAY8p3x8/JARCWZYvSr2nx62bvOqoxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mZCOm/btsMAY8p3x8/JARCWZYvSr2nx62bvOqoxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmZCOm%2FbtsMAY8p3x8%2FJARCWZYvSr2nx62bvOqoxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1518&quot; height=&quot;1160&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.37.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.48.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgIOUj/btsMAkYoQ5X/caUhkfebLEKSsCwRRrgnV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgIOUj/btsMAkYoQ5X/caUhkfebLEKSsCwRRrgnV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgIOUj/btsMAkYoQ5X/caUhkfebLEKSsCwRRrgnV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgIOUj%2FbtsMAkYoQ5X%2FcaUhkfebLEKSsCwRRrgnV1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1448&quot; height=&quot;1258&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.01.48.png&quot; data-origin-width=&quot;1448&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.02.02.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;1262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dW6hV2/btsMALuDu3g/BtOjPmzRVqnGRRt9MI54Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dW6hV2/btsMALuDu3g/BtOjPmzRVqnGRRt9MI54Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dW6hV2/btsMALuDu3g/BtOjPmzRVqnGRRt9MI54Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdW6hV2%2FbtsMALuDu3g%2FBtOjPmzRVqnGRRt9MI54Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1528&quot; height=&quot;1262&quot; data-filename=&quot;스크린샷 2025-03-02 오후 6.02.02.png&quot; data-origin-width=&quot;1528&quot; data-origin-height=&quot;1262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;자 그럼 같이 해볼까요? 우선 0부터 10까지 숫자를 print 해보는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1740906143796&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for(var i=0; i &amp;lt; 10 ;i++){
   print(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하나하나 살펴볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. var i=0 은 말그대로 i라는 변수에 0이라는 정수를 정의해줬어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. i &amp;lt; 10 이건 무슨뜻일까? 바로 반복이 행해지기 위한 조건이에요. 즉 i변수에 담긴 값이 10보다 작을때만 반복을 진행할께. 라는 뜻이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. i++ 은 반복문이 한번 실행될때 마다 +1씩 증가된다는 뜻이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4. 조건이 성립할때만 {} 안에 있는 print(i)가 실행되게 되어있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;조금 응용을 해볼께요. 그럼 숫자를 역순으로 출력할수도 있지 않을까요?&lt;/p&gt;
&lt;pre id=&quot;code_1740906192303&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for(var i=10; i &amp;gt; 0 ;i--){
   print(i);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이런식으로 역순으로 출력도 가능해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;조금만 응용한다면 다양한 형태로 반복문을 작성할수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;if와 마찬가지로 for문도 당연히 중첩이 가능해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740906219789&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for(var a=0; a &amp;lt; 10 ;a++){
   print(a);
   for(var b=0; b &amp;lt; 10 ;b++){
		   print(b);
		   ...
		   
		}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;마찬가지로 과도한 반복문의 중첩은 코드를 읽을때 가독성이 굉장히 떨어지기 때문에 주의가 필요해요~!&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;좀더 다른 방식의 for문을 작성할수도 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740906240781&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var fruits = ['사과','딸기','포도'];
for(var f in fruits){
  print(f);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1740906247847&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var fruits = ['사과','딸기','포도'];
fruits.forEach((f){
 print(f);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이와 같이 for in 과 foreach를 쓸수도 있어.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;fruits 라는 과일 목록이 있고 이 안에 있는 것들을 순차적으로 반복해서 출력해주는 예제이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우선 이런 방법도 있구나 하고 넘어가도록해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;뒤에서 리스트 라는 자료형을 배울때 다시 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;마지막으로 오늘 배운 내용을 사용해보고 넘어가도록 해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;실습을 해보겠습니다. if 문과 for문을 사용해서 숫자를 출력해보는 거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;김00, 이00, 박00 라는 사람이 있고 이사람들을 각각 이름을 반복 출력하는 예제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두가지 예제를 준비했는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1번예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1740906331723&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자'; 
  for(var i=0; i &amp;lt;= 10; i++){
     if(name == '김과자'){
       print(name);
     }else if(name == '이과자'){
       print(name);
     }else{ //그 이외의 전부
       print(name);
     }
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2번예제&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1740906357604&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
	var name = '김과자';
  if(name == '김과자'){
     for(var i=0; i &amp;lt;= 10; i++){
	     print(name);
     }
   }else if(name == '이과자'){
     for(var i=0; i &amp;lt;= 10; i++){
	     print(name);
     }
   }else{ //그 이외의 전부
     for(var i=0; i &amp;lt;= 10; i++){
	     print(name);
     }
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;똑같은 출력결과가 나오지만 코드의 효율적인 부분을 따져보면 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1번예제의 장점은 작성시점에 굉장히 깔끔하다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;for 문과 if문을 한번만 작성하면되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하지만 실제 실행시에는 for문의 반복횟수에 따라 if조건문의 조건 검사도 비례해서 반복된다는 것이에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;10번 반복 한다면, 10번 조건을 검사한다는 뜻이되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;100번 1000번 반복한다면??? 2번예제는 보기에는 비효율적으로 보이지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;막상 실행해보면 if 조건문을 처음 한번만 거치고&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그에 따른 반복문도 한번만 거치게 되어서 효율적이라 볼수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt; &amp;zwj;♂️ &quot;이런 예제들 처럼 &amp;hellip; 문법을 자유자재로 다루려면 시간이 많이 걸리겠네요&amp;hellip;&quot;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;시간을 내서 몇번만 연습해도 금방 익숙해질겁니다. 걱정마세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이렇게 해서 오늘은 조건문과 반복문에 대해서 알아봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아주 기본중에 기본이고 자주 사용되는 코드이니까 확실히 익혀두는게 좋겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Mobile Dev/코드와트 코딩강의</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/190</guid>
      <comments>https://kimback03.tistory.com/190#entry190comment</comments>
      <pubDate>Sun, 2 Mar 2025 18:18:34 +0900</pubDate>
    </item>
    <item>
      <title>코딩 강의2 - flutter dart 앱개발 숫자와 문자 그리고 타입 with ai(gpt)</title>
      <link>https://kimback03.tistory.com/189</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.27.50.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkbybJ/btsMv7kklYX/Do5AnqC2KAvfIkmHrCoiX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkbybJ/btsMv7kklYX/Do5AnqC2KAvfIkmHrCoiX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkbybJ/btsMv7kklYX/Do5AnqC2KAvfIkmHrCoiX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkbybJ%2FbtsMv7kklYX%2FDo5AnqC2KAvfIkmHrCoiX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;914&quot; height=&quot;526&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.27.50.png&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*아래의 글은 유튜브 코드와트 채널에서 연재되는 강의를 글로 작성한 내용입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*대화형식으로 진행되며, ai chat GPT를 활용한 코딩이 핵심입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*시대에 맞게 코딩.개발의 조력자로써 ai를 잘 활용것이 목표입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/gLYu_CCW1oM&quot;&gt;https://youtu.be/gLYu_CCW1oM&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=gLYu_CCW1oM&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bNdQZb/hyYjFRsWMd/ktjmXfppkHuIWHo66qO731/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=112_490_982_564,https://scrap.kakaocdn.net/dn/bhCvL4/hyYjzDHEmq/rO7HomufzkmDtFT1r8L17K/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=112_490_982_564&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;2화 dart/flutter 배우기 숫자와 문자 그리고 타입&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/gLYu_CCW1oM&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시간에 여러가지 언어를 실행해 보는 시간을 가졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 언어들은 생각보다 비슷한 구조와 문법을 가지고 있기 때문에 생각보다 복잡하거나 어렵지않았을거에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이제부터 우리는 이중에서 한가지를 선택해서 공부해 볼텐데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 언어를 먼저 배우게 될까요? 바로 dart 라는 언어 입니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &quot;왜 수많은 언어중에서 dart 언어를 먼저 배우는 건가요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dart를 배우는 이유는 바로 우리가 이어서 배울 앱개발을 쉽게 하기 위해서에요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 매일같이 사용하는 스마트폰은 android나, ios 라는 두가지 방식의 환경이 만들어져 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 dart 언어를 통해서 android나 ios 에서 실행할수 있는 앱을 한번에 만들수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♂️&quot;그럼 dart 라는언어가 제일 좋은건가 보군요!&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아쉽게도 제일 좋은 언어라는건 없어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에 맞게 환경에 맞게 각각의 언어들을 잘 활용하는것이 중요해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 오늘은 지난시간에 이어서 무작정 실행하기 2번째 시간입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 숫자와 문자에 대해서 배워볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 시간에 변수에 대해서 배워봤죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 말그대로 변할수 있는 값이나 정보를 담는 일종의 상자라고 배웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;box-7186750_1280.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vtltp/btsMwsVZEQz/BfqIDCKTkw1sHxDkwJTimk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vtltp/btsMwsVZEQz/BfqIDCKTkw1sHxDkwJTimk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vtltp/btsMwsVZEQz/BfqIDCKTkw1sHxDkwJTimk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvtltp%2FbtsMwsVZEQz%2FBfqIDCKTkw1sHxDkwJTimk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;358&quot; height=&quot;311&quot; data-filename=&quot;box-7186750_1280.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1113&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상자안에는 이것도 넣고 저것도 넣고 원하는걸 집어 넣을수 있는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난시간에는 &amp;lsquo;문자&amp;rsquo; 이와같이 따옴표를 통해 문자열을 만들어준거 기억하시죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'홍길동' 이런식으로 문자를 만들어서 상자에 담아줬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 숫자는 어떻게 하면 될까요? 너무 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자는 그냥 상자에 담아주시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740456660089&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var name = '홍길동';
var num = 123;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♀️ &quot;왜 문자는 따옴표를 붙이고 숫자는 그냥 넣어요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 질문이에요. 이건 그냥 정해진 규칙이라고 생각하시면 되요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로도 왜 이렇게 하지? 라는 생각이 들때에는 우리가 개발할때 지켜야 할 규칙이다. 라고 생각하면 되겠어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자를 좀 더 세분화 해볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자는 1,2,3,4&amp;hellip; 같은수를 나타내는 &lt;b&gt;정수&lt;/b&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.14, 1.5 같은수를 나타내는 &lt;b&gt;소수&lt;/b&gt;가 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 dart에서는 int와 double 이라는 &lt;b&gt;타입&lt;/b&gt;으로 나뉠수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &amp;zwj;♂️ &quot;타입이 뭔가요?&amp;hellip; int, double &amp;hellip; 온통 어려운 말뿐이네요.&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어려운 말이 나왔죠? 타입은 인간, 기계, 동물, 식물 처럼 구분하기위한 하나의 기준이라고 생각하면 되요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 타입에 대해서 자세하게 알아보도록 해요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;gpt 활용&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.12.29.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bniqsI/btsMt8Y8Ym7/iKdMO2p2tXqVYFFzIyljVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bniqsI/btsMt8Y8Ym7/iKdMO2p2tXqVYFFzIyljVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bniqsI/btsMt8Y8Ym7/iKdMO2p2tXqVYFFzIyljVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbniqsI%2FbtsMt8Y8Ym7%2FiKdMO2p2tXqVYFFzIyljVK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1584&quot; height=&quot;186&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.12.29.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.12.38.png&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brZyXt/btsMvNzAwFV/dGYW97xk7WLkBzQN1M9IY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brZyXt/btsMvNzAwFV/dGYW97xk7WLkBzQN1M9IY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brZyXt/btsMvNzAwFV/dGYW97xk7WLkBzQN1M9IY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrZyXt%2FbtsMvNzAwFV%2FdGYW97xk7WLkBzQN1M9IY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1546&quot; height=&quot;210&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.12.38.png&quot; data-origin-width=&quot;1546&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.12.53.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1qR0A/btsMvQprPge/dCRK6MsqGAu9CMhvZ13Odk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1qR0A/btsMvQprPge/dCRK6MsqGAu9CMhvZ13Odk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1qR0A/btsMvQprPge/dCRK6MsqGAu9CMhvZ13Odk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1qR0A%2FbtsMvQprPge%2FdCRK6MsqGAu9CMhvZ13Odk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1570&quot; height=&quot;768&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.12.53.png&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.13.16.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLN4Ey/btsMwyoluAU/1wQgLUNz1kC6DjOSWkwmXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLN4Ey/btsMwyoluAU/1wQgLUNz1kC6DjOSWkwmXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLN4Ey/btsMwyoluAU/1wQgLUNz1kC6DjOSWkwmXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLN4Ey%2FbtsMwyoluAU%2F1wQgLUNz1kC6DjOSWkwmXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;158&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.13.16.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.13.37.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cvZsZR/btsMvTsWBjf/KtSFw2iYb5c3Lxl7au7QlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cvZsZR/btsMvTsWBjf/KtSFw2iYb5c3Lxl7au7QlK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cvZsZR/btsMvTsWBjf/KtSFw2iYb5c3Lxl7au7QlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcvZsZR%2FbtsMvTsWBjf%2FKtSFw2iYb5c3Lxl7au7QlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1670&quot; height=&quot;694&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.13.37.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보자구, 이 상자안에는 한가지 종류의 물건만 넣어야 안전하게 꺼내쓸수있도록 만들어져있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 우리가 name 이라는 라벨이 붙은 상자를 꺼냈는데 막상 꺼내고 보니 그 안에 숫자가 들어있다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굉장히 당황스럽겠지? 마찬가지로 프로그램에서는 이런상황에서 오류를 만날수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 걱정할 필요없지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대의 개발도구들은 똑똑하기 때문에 오류상황을 대부분 사전에 막아 주기 때문이죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.15.24.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;230&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o2YRI/btsMwpkGNKy/5JdJs5mk1TPXgFIhjgW6Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o2YRI/btsMwpkGNKy/5JdJs5mk1TPXgFIhjgW6Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o2YRI/btsMwpkGNKy/5JdJs5mk1TPXgFIhjgW6Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo2YRI%2FbtsMwpkGNKy%2F5JdJs5mk1TPXgFIhjgW6Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1530&quot; height=&quot;230&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.15.24.png&quot; data-origin-width=&quot;1530&quot; data-origin-height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;그런데 이번에는 왜 var이 아닌 int, String 같은 이상한 문자를 쓰는건가요?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은질문입니다! 지난 시간에는 특수한 상자인 &lt;b&gt;var&lt;/b&gt; 이라는 키워드를 통해 이름을 넣어두고 사용했어요. 기억나시죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;var&lt;/b&gt; 은 특수한 상자로써 문자를 넣으면 문자타입으로 정해지고, 숫자를 넣으면 숫자타입으로 정해지는 똑똑한 상자에요. 그래서 특별한 경우가 아니면 우리는 &lt;b&gt;var&lt;/b&gt;을 사용해서 변수를 선언할 예정이에요.(편의를 위해)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실습&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 그럼 이론적인걸 배웠으니 실습을 해볼께요. 실습은 마찬가지로 웹상의 playground 우리의 놀이터에서 해볼거구요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://dartpad.dev&quot;&gt;https://dartpad.dev&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1740457066638&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DartPad&quot; data-og-description=&quot;&quot; data-og-host=&quot;dartpad.dev&quot; data-og-source-url=&quot;https://dartpad.dev&quot; data-og-url=&quot;https://dartpad.dev&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dartpad.dev&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dartpad.dev&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DartPad&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dartpad.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.17.57.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nWrA9/btsMwzANGVR/HKkVCqADWUSVSb2CeRJxcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nWrA9/btsMwzANGVR/HKkVCqADWUSVSb2CeRJxcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nWrA9/btsMwzANGVR/HKkVCqADWUSVSb2CeRJxcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnWrA9%2FbtsMwzANGVR%2FHKkVCqADWUSVSb2CeRJxcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;992&quot; height=&quot;568&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.17.57.png&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;까만 화면이 열려서 당황했을거에요. 여기가 앞으로 놀게될 우리의 코딩놀이터 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 지난번 내용을 복습을 해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름을 변수에 넣고 콘솔창에 출력해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 시간을 드릴께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;복습&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1740457107272&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자';
  print(name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;*위 코드와 같이 작성하면 됩니다.&amp;nbsp;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 이 코드에 숫자까지 사용해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 var name = &amp;lsquo;김과자&amp;rsquo;; 밑에 코드를 작성해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;눈치 채셨겠지만 코드 한줄이 끝나면 항상 ;(세미콜론)으로 마무리를 해주셔야 해요. 규칙입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740457178839&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자';
  var number = 100;
  print(name);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 100을 변수상자에 담았습니다. 이 상자는 이제부터 int 라는 타입으로 정해진상태가 됩니다.(보이진 않지만)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 숫자도 한번 출력해볼께요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740457205255&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자';
  var number = 100;
  print(name);
  print(number);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고맙게도 print 가 문자든 숫자든 잘 출력해주고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.20.19.png&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o922H/btsMvA8lwrI/7JXVI3Xo0qVbk680W4onwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o922H/btsMvA8lwrI/7JXVI3Xo0qVbk680W4onwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o922H/btsMvA8lwrI/7JXVI3Xo0qVbk680W4onwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo922H%2FbtsMvA8lwrI%2F7JXVI3Xo0qVbk680W4onwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;208&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.20.19.png&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이왕 숫자를 사용했으니 사칙연산을 해보도록 할께요.&lt;/p&gt;
&lt;pre id=&quot;code_1740457246522&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자';
  var number = 100;
  var plusNumber = 100 + 100;
  var minusNumber = 100 - 50;
  var muxNumber = 100 * 3;
  var divNumber = 100 / 10;
  
  print(name);
  print(number);
  print(plusNumber);
  print(minusNumber);
  print(muxNumber);
  print(divNumber);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사칙연산에 사용되는 기호를 잘 익혀두세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하면서 항상 줄을 잘 맞추고 깔끔한 코드가 작성되도록 노력해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가요? 콘솔에 잘 출력이 되고 있죠?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.22.07.png&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3BGye/btsMw0SwZVm/HZDpSkZoeaH8I9q5kClnQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3BGye/btsMw0SwZVm/HZDpSkZoeaH8I9q5kClnQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3BGye/btsMw0SwZVm/HZDpSkZoeaH8I9q5kClnQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3BGye%2FbtsMw0SwZVm%2FHZDpSkZoeaH8I9q5kClnQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;328&quot; data-filename=&quot;스크린샷 2025-02-25 오후 1.22.07.png&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러고보니 int 타입의 정수만 출력해줬네요? 그럼 소수는 어떻게 만들고 어떻게 출력하면 될까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나아가서 오늘 배운 int타입, double 타입을 활용해볼까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740457399239&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자';
  var number = 100;
  var plusNumber = 100 + 100;
  var minusNumber = 100 - 50;
  var muxNumber = 100 * 3;
  var divNumber = 100 / 10;
  
  //변수 선언할때 int타입, double타입을 사용
  int iNumber = 123;
  double dNumber = 3.14;
  String sName = '김구릉';
  
  print(name);
  print(number);
  print(plusNumber);
  print(minusNumber);
  print(muxNumber);
  print(divNumber);
  
  print(iNumber);
  print(dNumber);
  print(sName);
  
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤가요? 생각보다 쉽게 코드로 작성할수 있죠?&lt;/p&gt;</description>
      <category>Mobile Dev/코드와트 코딩강의</category>
      <category>Android</category>
      <category>DART</category>
      <category>flutter</category>
      <category>IOS</category>
      <category>강의</category>
      <category>앱개발</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/189</guid>
      <comments>https://kimback03.tistory.com/189#entry189comment</comments>
      <pubDate>Wed, 26 Feb 2025 14:28:31 +0900</pubDate>
    </item>
    <item>
      <title>코딩 강의1 - 앱개발 (flutter, dart, android, ios) 일단 실행해 봅시다.</title>
      <link>https://kimback03.tistory.com/188</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.48.21.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wJt1R/btsMvdkXbhW/mgJSpKVKoGSiRbjeB9zv3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wJt1R/btsMvdkXbhW/mgJSpKVKoGSiRbjeB9zv3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wJt1R/btsMvdkXbhW/mgJSpKVKoGSiRbjeB9zv3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwJt1R%2FbtsMvdkXbhW%2FmgJSpKVKoGSiRbjeB9zv3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;462&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.48.21.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*아래의 글은 유튜브 코드와트 채널에서 연재되는 강의를 글로 작성한 내용입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*대화형식으로 진행되며, ai chat GPT를 활용한 코딩이 핵심입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;*시대에 맞게 코딩.개발의 조력자로써 ai를 잘 활용것이 목표입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;a href=&quot;https://youtu.be/kD9Q6sgoaLU&quot;&gt;https://youtu.be/kD9Q6sgoaLU&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=kD9Q6sgoaLU&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bxJfOv/hyYmQXMfSR/96rDqKqXr0uCZHH3vbqdc1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=112_498_426_558,https://scrap.kakaocdn.net/dn/lSeyv/hyYncT1uz5/vcIlDJNKN57Ij1vDquebc1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=112_498_426_558&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;1화 코딩 개발 일단 시작해보는거지뭐~&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/kD9Q6sgoaLU&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드와트에 잘오셨습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞으로 여러분은 코딩을 배우고 나아가서 앱개발까지 해볼예정이에요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요즘 주변에 코딩 / 개발에 대해서 관심을 가지는 경우가 많더라구요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 어딜가든 이런광고가 눈에 띄기도 하구요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RmkiM/btsMt7sefrC/dJVCvMdLfIv0yxQSdpfPOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RmkiM/btsMt7sefrC/dJVCvMdLfIv0yxQSdpfPOk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;286&quot; data-origin-height=&quot;198&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.17.45.png&quot; style=&quot;width: 45.155658%; margin-right: 10px;&quot; data-widthpercent=&quot;45.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RmkiM/btsMt7sefrC/dJVCvMdLfIv0yxQSdpfPOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRmkiM%2FbtsMt7sefrC%2FdJVCvMdLfIv0yxQSdpfPOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;286&quot; height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DNCfi/btsMwfJabMy/cXSonqRAhftgffhgARu0Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DNCfi/btsMwfJabMy/cXSonqRAhftgffhgARu0Kk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;198&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.17.49.png&quot; style=&quot;width: 53.681551%;&quot; data-widthpercent=&quot;54.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DNCfi/btsMwfJabMy/cXSonqRAhftgffhgARu0Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDNCfi%2FbtsMwfJabMy%2FcXSonqRAhftgffhgARu0Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cISsHC/btsMuvmbs7s/blDA8u56hrk1aELZ2bUFi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cISsHC/btsMuvmbs7s/blDA8u56hrk1aELZ2bUFi1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;260&quot; data-origin-height=&quot;148&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.17.54.png&quot; style=&quot;width: 50.230013%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;50.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cISsHC/btsMuvmbs7s/blDA8u56hrk1aELZ2bUFi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcISsHC%2FbtsMuvmbs7s%2FblDA8u56hrk1aELZ2bUFi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;260&quot; height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M8mWQ/btsMvzH8bde/1OFRA57WiBEbw58CuEy6Z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M8mWQ/btsMvzH8bde/1OFRA57WiBEbw58CuEy6Z0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;160&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.18.01.png&quot; style=&quot;width: 48.607197%; margin-top: 10px;&quot; data-widthpercent=&quot;49.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M8mWQ/btsMvzH8bde/1OFRA57WiBEbw58CuEy6Z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM8mWQ%2FbtsMvzH8bde%2F1OFRA57WiBEbw58CuEy6Z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;272&quot; height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또 25년 부터는 코딩이 교과목으로 편성 된다는 이야기도 있죠?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-token-index=&quot;0&quot;&gt;그만큼 코딩의 중요성이 강조되는 시대인것 같아요.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-token-index=&quot;0&quot;&gt; 바로 코딩을 통해 사회에서도, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-token-index=&quot;0&quot;&gt;우리 일상에서도 논리적으로 사고하는법, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-token-index=&quot;0&quot;&gt;그에 따른 합리적인 결과 도출을 할수있도록 돕는것 이것이 궁극적인 목표라는 생각이 듭니다. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-token-index=&quot;0&quot;&gt;그래서 되도록 어릴때 배울수록 좋겠죠? 또 나아가서는 개발자로 취업 기회를 얻을 수 있구요. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot; data-token-index=&quot;0&quot;&gt;코딩을 배우고 업무 자동화를 통해서 업무력도 올릴수도 있구요.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;그럼 여러분들은 이런생각을 하고 있을거에요.&lt;br /&gt;&quot;나도 과연 할수있을까?&amp;rdquo; 라는 고민을 하고 계실텐데요. &lt;br /&gt;우선은 제가 추천드리는 방식으로 한번 그냥 따라해 보는겁니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;고민은 시간만 늦출뿐이죠. &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;뭐든 그렇듯이 일단 시작해 보는겁니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.22.04.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uc4XC/btsMt76PFs6/GKtMxlXHqekw8OXEzLee7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uc4XC/btsMt76PFs6/GKtMxlXHqekw8OXEzLee7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uc4XC/btsMt76PFs6/GKtMxlXHqekw8OXEzLee7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fuc4XC%2FbtsMt76PFs6%2FGKtMxlXHqekw8OXEzLee7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;281&quot; height=&quot;276&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.22.04.png&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 첫번째 시간으로 복잡한 설명은 일단 접어두도록 하구요. (차차설명)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일단 무작정 따라서 코딩하고 무언가 결과를 만들어내는게 목표입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 우리는 시대에 맞게 GPT 등의 ai 기술을 최대치로 활용할 예정입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여담이지만 gpt같은 ai를 사용하는것은 박사급의 조수를 부리는것과 동일하다고 합니다.  &lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;저희 강의에서는 기본적인 문법을 배워볼예정이구요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;복잡한 연산, 필요없는 수식은 사용하지 않을거에요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대한 코딩에 흥미를 가질수 있도록 하는게 목표입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;외울필요도 없습니다. 그냥 복사 / 붙혀넣기 하셔도 되구요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;편하게 그렇게 시작해보는 겁니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자 그럼 시작해보겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선은 저희가 배울 언어에 대해서 간단하게 소개해볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;앱개발을 위한 대표언어들&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;자바 (가장 많이 쓰이는 대표 프로그래밍 언어)&lt;/li&gt;
&lt;li&gt;kotlin (안드로이드 앱 개발에 쓰이는 언어)&lt;/li&gt;
&lt;li&gt;swift (ios 앱개발에 쓰이는 언어)&lt;/li&gt;
&lt;li&gt;dart (flutter 앱개발에 쓰이는 언어)&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리의 목표는 기초 문법 공부를 통해 프로그래밍방식을 이해하고, 나아가서 앱개발까지 해볼 예정이에요 &lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요새 스마트폰 없는 사람 찾기가 더 힘들겠죠?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 강의를 통해 앱개발을 하고 흥미느끼도록 돕는게 목표입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;아니 왜 이렇게 많은 언어를 배움?&amp;rdquo; 하고 느끼실수 있겠지만&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;특정 언어를 배우고 나면 다른 언어를 배우기가 많이 쉽습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;쉽게 말해서 절반 이상은 먹고 들어간다고 생각하시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 절대 부담 가지실 필요가 없구요. 따라하다보면 저절로 익혀질거에요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음에 어떤언어를 선택하셔도 상관없습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선은 가장 기본적으로 많이 쓰이는 자바언어로 시작해볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 이사이트에 접속 하세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;여러분은 로컬 pc에(여러분의 컴퓨터) 아무것도 설치할 필요가 없어요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;연습은 이런식으로 접속 해서 바로 연습해볼 겁니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 왜 로컬 환경이 아니라 웹 환경을 사용하는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;진행&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리는 처음부터 복잡한 개발 환경을 설정하지 않고,&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웹 브라우저에서 바로 코드를 실행할 수 있도록 온라인 자바 실행기를 사용할 것입니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;과장되게 말하면 환경셋팅하다가 하루가 지날수도 있구요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이것저것 오류를 해결하다보면 이미 거기서 여러분은 &amp;ldquo;에이 안해~&amp;rdquo; 라는 말이 절로 나오게 될겁니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자바사이트&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자바언어를 학습하기 위한 웹 환경의 놀이터라고 생각해주시면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev.java/playground/&quot;&gt;https://dev.java/playground/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;당황스러운창이 열리는데요. 일단 냅다&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;RUN&lt;/b&gt;을 눌러볼게요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.14.55.png&quot; data-origin-width=&quot;2330&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9fmt5/btsMvzVDo4d/AnQjj0uRAWhPicbRogPBuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9fmt5/btsMvzVDo4d/AnQjj0uRAWhPicbRogPBuK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9fmt5/btsMvzVDo4d/AnQjj0uRAWhPicbRogPBuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9fmt5%2FbtsMvzVDo4d%2FAnQjj0uRAWhPicbRogPBuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2330&quot; height=&quot;728&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.14.55.png&quot; data-origin-width=&quot;2330&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj2COG/btsMweQZISy/k9OikWDihmITWdkuiJVKD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj2COG/btsMweQZISy/k9OikWDihmITWdkuiJVKD0/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;232&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.16.14.png&quot; style=&quot;width: 68.355339%; margin-right: 10px;&quot; data-widthpercent=&quot;69.16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj2COG/btsMweQZISy/k9OikWDihmITWdkuiJVKD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj2COG%2FbtsMweQZISy%2Fk9OikWDihmITWdkuiJVKD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rd2C3/btsMuUsnDRi/0HXISAlHDvZ7IqkEwrlKWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rd2C3/btsMuUsnDRi/0HXISAlHDvZ7IqkEwrlKWK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;144&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.15.38.png&quot; style=&quot;width: 30.48187%;&quot; data-widthpercent=&quot;30.84&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rd2C3/btsMuUsnDRi/0HXISAlHDvZ7IqkEwrlKWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frd2C3%2FbtsMuUsnDRi%2F0HXISAlHDvZ7IqkEwrlKWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;186&quot; height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;뭔지모르겠지만 결과가 출력되었네요? 그쵸?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;헬로우 Duke? 누구죠? 우리 이름으로 바꿔볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;각자 자기 이름으로 바꿔서 입력해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(코딩이나 개발을 배우면서 꿀팁을 드리자면 꼭 하라는대로 해야한다는것이에요&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대한 오타를 내면 안되요. 문법에 매우 민감하기 때문에 . 하나만 잘못찍어도 에러가 발생해요.)&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.17.02.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYH0Pd/btsMuLh0XCW/wNF43TVlJh2x9A8zJxuHo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYH0Pd/btsMuLh0XCW/wNF43TVlJh2x9A8zJxuHo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYH0Pd/btsMuLh0XCW/wNF43TVlJh2x9A8zJxuHo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYH0Pd%2FbtsMuLh0XCW%2FwNF43TVlJh2x9A8zJxuHo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;122&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.17.02.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;저는 이렇게 바꾸었구요. 실행해 보겠습니다. 어떻게 하냐구요? 다시 Run 을 눌러주시면 됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.19.47.png&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZkDvq/btsMu9bRAFM/APiAsQeQABKHMBQn9PjF40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZkDvq/btsMu9bRAFM/APiAsQeQABKHMBQn9PjF40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZkDvq/btsMu9bRAFM/APiAsQeQABKHMBQn9PjF40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZkDvq%2FbtsMu9bRAFM%2FAPiAsQeQABKHMBQn9PjF40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;206&quot; height=&quot;168&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.19.47.png&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.20.03.png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rYSxd/btsMuunbn3t/MoGkGkdhwf5A7sYMTomxY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rYSxd/btsMuunbn3t/MoGkGkdhwf5A7sYMTomxY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rYSxd/btsMuunbn3t/MoGkGkdhwf5A7sYMTomxY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrYSxd%2FbtsMuunbn3t%2FMoGkGkdhwf5A7sYMTomxY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;228&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.20.03.png&quot; data-origin-width=&quot;548&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어때요? 신기하죠? 제 이름이 출력되었어요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 출력된 공간을 Console(콘솔) 창 이라고 불러요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리도 이름이 있듯이 이친구들도 다 이름이 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 우리가 이름을 변경할때 봤던 이부분있죠?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.17.02.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vktcr/btsMwo6XY6n/tDpw377s0yWE47E34kvC3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vktcr/btsMwo6XY6n/tDpw377s0yWE47E34kvC3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vktcr/btsMwo6XY6n/tDpw377s0yWE47E34kvC3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvktcr%2FbtsMwo6XY6n%2FtDpw377s0yWE47E34kvC3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;122&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.17.02.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;처음 보는 이 문자들이 굉장히 당황스러우실거라 생각이 드는데요. 하나씩 해석해볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리 박사조교 gpt를 사용해서 궁금증을 해결해볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;gpt에게 물어볼때에는 최대한 자세하게 앞뒤 정황 상세한 설명으로 물어보셔야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.30.39.png&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zPeKo/btsMugJy7CC/UMmiFM9p0NuKJjQE5EI2x1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zPeKo/btsMugJy7CC/UMmiFM9p0NuKJjQE5EI2x1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zPeKo/btsMugJy7CC/UMmiFM9p0NuKJjQE5EI2x1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzPeKo%2FbtsMugJy7CC%2FUMmiFM9p0NuKJjQE5EI2x1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1150&quot; height=&quot;724&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.30.39.png&quot; data-origin-width=&quot;1150&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자세하게 예제까지 추가해서 설명해줍니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;var은 variable 의 약자로 즉 변수를 의미 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 변수는 뭐죠? 뜻을 살펴볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.30.17.png&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbICfv/btsMwejc4gN/GvSRhOzulBfs50MTrsqSuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbICfv/btsMwejc4gN/GvSRhOzulBfs50MTrsqSuk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbICfv/btsMwejc4gN/GvSRhOzulBfs50MTrsqSuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbICfv%2FbtsMwejc4gN%2FGvSRhOzulBfs50MTrsqSuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;278&quot; height=&quot;128&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.30.17.png&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.30.25.png&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbTA9h/btsMuLbfLMp/HhLN6Ja7QJFiKj3fuPAYkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbTA9h/btsMuLbfLMp/HhLN6Ja7QJFiKj3fuPAYkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbTA9h/btsMuLbfLMp/HhLN6Ja7QJFiKj3fuPAYkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbTA9h%2FbtsMuLbfLMp%2FHhLN6Ja7QJFiKj3fuPAYkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1058&quot; height=&quot;148&quot; data-filename=&quot;스크린샷 2025-02-19 오후 4.30.25.png&quot; data-origin-width=&quot;1058&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;일종의 상자가 있다고 생각하고 이 상자 안에는 장남감도 넣을수 있고, &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;옷도 넣을수 있고, 책도 넣을수 있는 공간이라고 생각하면 되요. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;처음 할때 어려운 점은 실체가 없기 때문에 어려워요. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;최대한 상상력을 발휘 하셔야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.35.54.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPD6X/btsMvM8sppt/ZQckBpwK4YUkk30Frxdjmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPD6X/btsMvM8sppt/ZQckBpwK4YUkk30Frxdjmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPD6X/btsMvM8sppt/ZQckBpwK4YUkk30Frxdjmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPD6X%2FbtsMvM8sppt%2FZQckBpwK4YUkk30Frxdjmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1096&quot; height=&quot;134&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.35.54.png&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;그리고 하나만더 살펴볼께요. 이 요상한 문장은 또 뭘까요?&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.36.15.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck60Q1/btsMueyg66f/jM4DSb1Cv9M7dmkfzUelI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck60Q1/btsMueyg66f/jM4DSb1Cv9M7dmkfzUelI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck60Q1/btsMueyg66f/jM4DSb1Cv9M7dmkfzUelI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck60Q1%2FbtsMueyg66f%2FjM4DSb1Cv9M7dmkfzUelI0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;130&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.36.15.png&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이부분을 보고 추측하셨을거라 생각이 드는데요. 맞아요. 해당 코드는 Console 창에 출력하기 코드가 맞아요!&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이쯤되면 다른 문장을 출력하고 싶어지잖아요? 그쵸? 해볼께요. 다른건 건들지 마시구요 주의!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.36.37.png&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vcMJm/btsMuLvAghQ/n4GJyJ5LBbzJjeCaXKaa01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vcMJm/btsMuLvAghQ/n4GJyJ5LBbzJjeCaXKaa01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vcMJm/btsMuLvAghQ/n4GJyJ5LBbzJjeCaXKaa01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvcMJm%2FbtsMuLvAghQ%2Fn4GJyJ5LBbzJjeCaXKaa01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;122&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.36.37.png&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;정확히 이부분만 수정해주겠습니다. 지우고 제 닉네임을 써넣었구요. 실행해주겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.37.07.png&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cG1uR2/btsMvcfmiTp/ADCListY1YmfJzMktg7nP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cG1uR2/btsMvcfmiTp/ADCListY1YmfJzMktg7nP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cG1uR2/btsMvcfmiTp/ADCListY1YmfJzMktg7nP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcG1uR2%2FbtsMvcfmiTp%2FADCListY1YmfJzMktg7nP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;918&quot; height=&quot;270&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.37.07.png&quot; data-origin-width=&quot;918&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어때요 예상대로 제가 입력한 문장이 출력된걸 볼수 있죠?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제 응용을 해볼께요. &amp;ldquo;안녕하세요. 000 입니다.&amp;rdquo; 에서 000을 우리 아까 만들어둔 상자를 활용해볼께요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;바로 위에서 만든 name 변수를 사용하는것인데요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.37.23.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;134&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBvv7w/btsMufDVRJm/7KMX0vFUj8AocDbInAQfxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBvv7w/btsMufDVRJm/7KMX0vFUj8AocDbInAQfxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBvv7w/btsMufDVRJm/7KMX0vFUj8AocDbInAQfxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBvv7w%2FbtsMufDVRJm%2F7KMX0vFUj8AocDbInAQfxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;134&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.37.23.png&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;134&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;완성된문장은 바로 이겁니다. 여기서 이상한게 또 있는데요. 쌍따옴표라던가, + 기호라던가? 이상한게 있죠?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;gpt 조교에게 물어보겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.37.41.png&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;718&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uP8Xb/btsMuK4vQyy/kLUxYRkJvmAKCePHSVCyG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uP8Xb/btsMuK4vQyy/kLUxYRkJvmAKCePHSVCyG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uP8Xb/btsMuK4vQyy/kLUxYRkJvmAKCePHSVCyG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuP8Xb%2FbtsMuK4vQyy%2FkLUxYRkJvmAKCePHSVCyG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1174&quot; height=&quot;718&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.37.41.png&quot; data-origin-width=&quot;1174&quot; data-origin-height=&quot;718&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;쌍따옴표는 텍스트문자를 표현할때 쓰는 기호입니다. 문장을 시작할때 &amp;ldquo;로 시작하고 &amp;ldquo;로 끝내주면 되요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 + 는 뭘까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.38.13.png&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N9FsT/btsMuxYxnqz/BzEmc1cWjfBzkabw3d4zf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N9FsT/btsMuxYxnqz/BzEmc1cWjfBzkabw3d4zf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N9FsT/btsMuxYxnqz/BzEmc1cWjfBzkabw3d4zf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN9FsT%2FbtsMuxYxnqz%2FBzEmc1cWjfBzkabw3d4zf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1104&quot; height=&quot;462&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.38.13.png&quot; data-origin-width=&quot;1104&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;말그대로 + 의 의미를 생각하시면 되요. 플러스는 더하기죠? 따라서 문자들을 하나로 합쳐준다 라고 생각하면 됩니다. 이제 왜 &amp;ldquo; 쌍따옴표와 + 기호를 사용했는지 알겠죠?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번에는 dart 언어를 실행해볼께요. 인터넷상에서 dartpad 사이트에 접속해주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dartpad.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dartpad.dev/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1740457873859&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DartPad&quot; data-og-description=&quot;&quot; data-og-host=&quot;dartpad.dev&quot; data-og-source-url=&quot;https://dartpad.dev/&quot; data-og-url=&quot;https://dartpad.dev/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://dartpad.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dartpad.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DartPad&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dartpad.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;창이 열리면 당황하지 마시고, 마찬가지로 run을 눌러주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;무언가 실행이 될것이고 그 결과를 지켜보면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.39.18.png&quot; data-origin-width=&quot;1758&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ppz0m/btsMuS2w3Xq/JSUGiZGMrcLEahwdNzUg6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ppz0m/btsMuS2w3Xq/JSUGiZGMrcLEahwdNzUg6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ppz0m/btsMuS2w3Xq/JSUGiZGMrcLEahwdNzUg6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fppz0m%2FbtsMuS2w3Xq%2FJSUGiZGMrcLEahwdNzUg6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1758&quot; height=&quot;668&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.39.18.png&quot; data-origin-width=&quot;1758&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;콘솔창에 출력이 잘되고 있죠.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;조금 수정해볼께요. 이 부분들을 지워주시고 지울때는 세심하게~&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 우리가 원하는 형태로 작성해줄께요. 아까하고 패턴이 거의 동일합니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;변수도 만들어주시고 이름도 넣어주세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;print 를 통해서 출력을 하는것도 똑같습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740451587496&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void main() {
  var name = '김과자';
  print('안녕하세요. 김과자 입니다.');
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나 더 해볼까요? 이번에는 kotlin 이라고 하는 언어로 이동 해볼게요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://play.kotlinlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://play.kotlinlang.org/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1740457921123&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Kotlin Playground: Edit, Run, Share Kotlin Code Online&quot; data-og-description=&quot; &quot; data-og-host=&quot;play.kotlinlang.org&quot; data-og-source-url=&quot;https://play.kotlinlang.org/&quot; data-og-url=&quot;https://play.kotlinlang.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJkUnI/hyYjvHiyHy/uWlWzHYryTbOU0GuirEktK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400&quot;&gt;&lt;a href=&quot;https://play.kotlinlang.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://play.kotlinlang.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJkUnI/hyYjvHiyHy/uWlWzHYryTbOU0GuirEktK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Kotlin Playground: Edit, Run, Share Kotlin Code Online&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;play.kotlinlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;kotlin 은 안드로이드 개발에 주로 쓰이고 있죠. 마찬가지로 run을 눌러 실행하고 결과를 지켜보세요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 나서 동일한 패턴으로 수정해주시고,&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다시한번 실행해볼게요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.40.52.png&quot; data-origin-width=&quot;2276&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUQ1D8/btsMvbnjPk6/ywVjgnoBV6ytjTsrwPrxwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUQ1D8/btsMvbnjPk6/ywVjgnoBV6ytjTsrwPrxwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUQ1D8/btsMvbnjPk6/ywVjgnoBV6ytjTsrwPrxwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUQ1D8%2FbtsMvbnjPk6%2FywVjgnoBV6ytjTsrwPrxwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2276&quot; height=&quot;834&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.40.52.png&quot; data-origin-width=&quot;2276&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.40.59.png&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JhBAW/btsMuv7AehF/c0cQEuXs00NLskGiaSNnxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JhBAW/btsMuv7AehF/c0cQEuXs00NLskGiaSNnxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JhBAW/btsMuv7AehF/c0cQEuXs00NLskGiaSNnxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJhBAW%2FbtsMuv7AehF%2Fc0cQEuXs00NLskGiaSNnxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;272&quot; height=&quot;182&quot; data-filename=&quot;스크린샷 2025-02-25 오전 11.40.59.png&quot; data-origin-width=&quot;272&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1740451597564&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun main() {
    var name = &quot;김과자&quot;;
    println(&quot;Hello, world!!!&quot; + name)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어떤가요?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;거의 동일한 문법으로 작성할수 있죠?&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘 여러가지 언어를 사용해서 간단한 코딩을 해봤는데요.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;제가 말씀드린대로 정말 쉽게 실행할수 있었을거에요.&lt;/p&gt;</description>
      <category>Mobile Dev/코드와트 코딩강의</category>
      <category>AI</category>
      <category>DART</category>
      <category>flutter</category>
      <category>IOS</category>
      <category>강의</category>
      <category>안드로이드</category>
      <category>유튜브강의</category>
      <category>코딩</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/188</guid>
      <comments>https://kimback03.tistory.com/188#entry188comment</comments>
      <pubDate>Tue, 25 Feb 2025 11:48:46 +0900</pubDate>
    </item>
    <item>
      <title>백그라운드 개발 후기(WorkManager, 포그라운드서비스)</title>
      <link>https://kimback03.tistory.com/187</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 블루스웨터소프트 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하며 겪은 이야기와 기술적인 이야기를 작성하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트단에서 사용할 특정 sdk를 개발하는 중에 급하게 연락이 왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;기존의 기능들을 백그라운드에서 동작하게 할수 있을까요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;네. 되긴 됩니다만...&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AOS, IOS 백그라운드 제한에 대해 말이 길어지게 되었고 관련 내용을 정리하여 보내주기로 합니다 &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google, Apple은 업데이트를 거듭할수록 백그라운드 동작에 대해 매우 엄격한 제제를 가하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그도 그럴것이 유저의 보안, 기기의 퍼포먼스적인 측면에서 백그라운드 동작은 매우 불리하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정앱이 나도 모르게 무한정 실행되고 있다고 생각해보시면 어떻게 될까요? 배터리는 배터리대로, 보이지도 않기에 무슨 앱이 어떻게 동작하고 있는지 뭘 수집하는건지 알기도 어렵습니다. 여타 여러가지 이유들로 인해 대세는 백그라운드 제한인 것 입니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 백그라운드 동작에 대해서 굉장히 관대했습니다. 특히나 안드로이드에서는 service 에 모든 처리를 위임한 백그라운드 앱들이 난무하였습니다. 안드로이드 8.0 OREO 버전을 시작으로 굉장히 엄격해진것으로 기억합니다. 런타임 퍼미션 같은 새로운 권한 관리가 생겼고, 백그라운드 실행을 위해 Foreground service 등이 필수로 등장했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.blueswt.com/71&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;오래전에 해당 관련 글을 자세하게 썼던 기억이있습니다.&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;포그라운드 서비스의 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 기존의 foreground service 구현을 확인해보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707300001765&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; ...&amp;gt;
    &amp;lt;service
        android:name=&quot;.MyMediaPlaybackService&quot;
        android:foregroundServiceType=&quot;mediaPlayback&quot;
        android:exported=&quot;false&quot;&amp;gt;
    &amp;lt;/service&amp;gt;
&amp;lt;/manifest&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메니페스트에 service 타입을 명시해야합니다. (target API에 따라서)&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;background-color: #e8f0fe; color: #303f9f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;앱에서 타겟팅하는 API 수준에 따라 앱 매니페스트에서 포그라운드 서비스를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;반드시&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;선언해야 할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #e8f0fe; color: #303f9f; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;API 수준 29 이상:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.android.com/develop/background-work/services/fg-service-types?hl=ko#location&quot;&gt;location&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서비스 유형을 사용하여 위치 정보를 사용하는 모든 포그라운드 서비스를 선언해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API 수준 30 이상:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.android.com/develop/background-work/services/fg-service-types?hl=ko#camera&quot;&gt;camera&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.android.com/develop/background-work/services/fg-service-types?hl=ko#microphone&quot;&gt;microphone&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;서비스 유형을 사용하여 카메라나 마이크를 사용하는 모든 포그라운드 서비스를 각각 선언해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API 수준 34 이상:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;모든 포그라운드 서비스는 서비스 유형과 함께 선언해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #e8f0fe; color: #303f9f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;포그라운드 서비스를 만들려고 하는데 매니페스트에서 해당 유형이 선언되지 않았다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;startForeground()&lt;span&gt;&amp;nbsp;&lt;/span&gt;호출 시 시스템에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.android.com/reference/android/app/MissingForegroundServiceTypeException?hl=ko&quot;&gt;MissingForegroundServiceTypeException&lt;/a&gt;이 발생합니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #e8f0fe; color: #303f9f; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;필요하지 않더라도 모든 포그라운드 서비스를 선언하고 서비스의 서비스 유형을 제공하는 것이 좋습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;xml&quot; style=&quot;color: #000000; text-align: left;&quot;&gt;&lt;code&gt;&amp;lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; ...&amp;gt;

&amp;nbsp; &amp;nbsp; &amp;lt;uses-permission android:name=&quot;android.permission.FOREGROUND_SERVICE&quot;/&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;lt;uses-permission android:name=&quot;android.permission.FOREGROUND_SERVICE_CAMERA&quot;/&amp;gt;

&amp;nbsp; &amp;nbsp; &amp;lt;application ...&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...
&amp;nbsp; &amp;nbsp; &amp;lt;/application&amp;gt;
&amp;lt;/manifest&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메니페스트에 권한을 요청해야 합니다. API28 이상을 타겟팅 한다면 반드시 권한을 작성해야합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;color: #000000; text-align: left;&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;class MyCameraService: Service() {
//...

&amp;nbsp; 	private fun startForeground() {
        val cameraPermission =
                ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            //권한 거부 처리
            return
        }

        try {
            val notification = NotificationCompat.Builder(this, &quot;CHANNEL_ID&quot;) //채널ID
                .build()
            ServiceCompat.startForeground(
                this,
                100,
                notification,
                if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.R) {
                    ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA //특정 타입 명세
                } else {
                    0
                },
            )
        } catch (e: Exception) {
            // ...
        }
&amp;nbsp; 	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 서비스에서 channel, notification 등을 생성후 startForegroundService를 실행하게 됩니다. 문서를 보면 아시겠지만 최근에 디테일한 부분이 굉장히 많이 바뀌었고 복잡해 졌습니다. 문서를 잘 보고 다시 작업 해야 합니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌되었든 ForegroundService를 써야 하는 이유는 명확했습니다. 백그라운드에서 동작시 최대한 동작이 보장되어야 하기 때문 입니다. (메모리 상황에 따라서 죽기도 함) 때문에 포그라운드 실행시 noti, channel 등을 생성하여 알림창을 띄우고 유저에게 동작을 알리게 됩니다. 공식 문서의 내용을 보다보니 &lt;a href=&quot;https://developer.android.com/about/versions/12/foreground-services?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;포그라운드 서비스 실행 제한&lt;/a&gt; 이라는 문서를 보게 되었습니다. 설마... &lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;Android&amp;nbsp;12를 타겟팅하는 앱은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://developer.android.com/about/versions/12/foreground-services?hl=ko#cases-fgs-background-starts-allowed&quot;&gt;몇 가지 특수한 사례&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;를 제외하고 백그라운드에서 실행되는 동안 더 이상&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://developer.android.com/guide/components/foreground-services?hl=ko&quot;&gt;포그라운드 서비스&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;를 시작할 수 없습니다. 앱이 백그라운드에서 실행되는 동안 포그라운드 서비스를 시작하려고 하지만 포그라운드 서비스가 예외 사례 중 하나를 충족하지 못하면 시스템에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #000000; text-align: start;&quot; href=&quot;https://developer.android.com/reference/android/app/ForegroundServiceStartNotAllowedException?hl=ko&quot;&gt;ForegroundServiceStartNotAllowedException&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124; text-align: start;&quot;&gt;이 발생합니다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글에서 매년 타겟 버전업을 강제로 시키고 있는 상황에서 이제 다른 방법으로 백그라운드 작업을 실행해라. 라는 말이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;WorkManager의 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하고있는 앱의 경우 문서에서 이야기 하는 몇가지 특수한 사례에 포함될것 같지 않았습니다.   때문에 WorkManager 를 사용하여 안전하게? 백그라운드 처리를 하기로 합니다. 사실 그동안에 WorkManager는 주기적인 예약 작업등에 많이 사용하도록 권장되어왔습니다.&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;심지어 앱이 꺼져도 백그라운드에서 작업을 처리 해야할때 유용합니다. (배치같이) WorkManager가 처리하는 지속적인 작업의 유형은 세 가지입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;즉시&lt;/b&gt;: 즉시 시작하고 곧 완료해야 하는 작업입니다. 신속하게 처리될 수 있습니다. &amp;nbsp;(약 3분이상)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;장기 실행&lt;/b&gt;: 더 오래(10분 이상이 될 수 있음) 실행될 수 있는 작업입니다. (약 10분이상)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지연 가능&lt;/b&gt;: 나중에 시작하며 주기적으로 실행될 수 있는 예약된 작업입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 앱이 해당되는 부분은 장기 실행 부분 입니다. 대량의 파일 읽기 쓰기나 DB처리, 네트워크처리 등 다양한 이유로 백그라운드 작업이 필요할수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;커스텀 worker 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Custom Worker - doWork 을 통해 장기 실행이 필요한 작업을 이곳에서 작성하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707354835054&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CustomWorker(var ctx: Context, var params: WorkerParameters) : CoroutineWorker(ctx, params) {
    private val TAG = &quot;CustomWorker&quot;

    override suspend fun doWork(): Result {
        val outputData = workDataOf(&quot;outputData&quot; to &quot;success&quot;)

        return try {
            //장기화 실행 포그라운드 처리
            setForeground(createForegroundInfo(ctx, &quot;starting...&quot;))

            initNotification(ctx)

            Log.d(TAG, &quot;=== 이미지 분석 시작 : ${Date()} ===&quot;)

            loadImagesAndDocument(ctx)
            Result.success(outputData)

        } catch (e: Exception) {
            Log.e(TAG, &quot;=== err CustomWorker ===&quot;)
            Result.failure()

        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포그라운드 처리를 위해 CoroutineWorker, setForeground 를 설정한뒤 notification, channel 등도 지정합니다. 기존의 foreground service 와 동일 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707355206127&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;private fun createForegroundInfo(ctx: Context, progress: String): ForegroundInfo {
        val id = CHANNEL_ID
        val title = &quot;장기화작업&quot;
        val cancel = &quot;취소&quot;

        val intent = WorkManager.getInstance(applicationContext)
            .createCancelPendingIntent(getId())

        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.O) {
            createChannel(ctx)
        }

        val notification = NotificationCompat.Builder(applicationContext, id)
            .setContentTitle(title)
            .setTicker(title)
            .setContentText(progress)
            .setSmallIcon(R.drawable.custom_icon)
            .setOngoing(true)
//            .addAction(R.drawable.ic_delete, cancel, intent)
            .build()

        return ForegroundInfo(NOTIFICATION_ID, notification)
    }


    fun createChannel(context: Context){
        if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.O) {
            val name = CHANNEL_NAME
            val description = CHANNEL_DESCRIPTION
            val importance = NotificationManager.IMPORTANCE_HIGH
            val channel = NotificationChannel(CHANNEL_ID, name, importance)
            channel.description = description

            val notificationManager =
                context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?
            
            notificationManager?.createNotificationChannel(channel)
        }

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Worker를 만들었다면 실행은 굉장히 간단합니다. uniqueWork를 통해 정책과 초기화 단계에서 작업할 워커를 생성하고 각종 제약조건을 걸어줍니다. 충전중일때 동작할것인지, 네트워크는 연결되어야 할지 등등 많은 옵션을 지정할수 있습니다. 마지막으로 Custom Worker를 셋팅하여 최종 enqueue 를 통해 실행을 시킵니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707356011263&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; /**
 * 백그라운드 작업 - Workmanager
 */
    private fun executeWithWorkManager(context: Context){

        val workManager = WorkManager.getInstance(context)

        var continuation = workManager
            .beginUniqueWork(
                WORK_NAME,
                ExistingWorkPolicy.REPLACE,
                OneTimeWorkRequest.from(initWorker::class.java) //초기화 작업을 위한 워커
            )

        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .setRequiresCharging(false)
            .build()

        val doBuilder = OneTimeWorkRequestBuilder&amp;lt;CustomWorker&amp;gt;()
            .setConstraints(constraints)
            .addTag(TAG_CUSTOM_WORK)
            .build()

        continuation = continuation.then(doBuilder)
        continuation.enqueue()

    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같이 Work Manager를 통해 장기화 실행 처리를 진행했습니다. 이 장기화 라는 말에 속아 무한정 돌것이라는 기대는 안하는게 좋습니다. &lt;span style=&quot;background-color: #f3c000;&quot;&gt;&lt;b&gt;동작시간은 최대시간 600초(10분) 정도가 한계입니다.   (IOS는 더 제한적입니다 약 3분...)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리글&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기기에 따라서 OS에 따라서 조금씩 다르지만 공통적인것은 구글이나 애플에서 정책적으로 백그라운드 제한을 심화 하고 있다는 것입니다. 그 덕에 개발자들은 변경된 정책을 보고 또 보고 업데이트 해야 합니다. 이러한 내용들을 사전 리서치하고 전달하는것 그리고 기획자를 잘 설득시키는것 또한 일이라고 하겠습니다. 안되는걸 되게 할수는 없지 않겠습니까? &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시나 쉬운게 없다고 생각하며... &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;참고 문서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://developer.android.com/about/versions/12/foreground-services?hl=ko&quot;&gt;https://developer.android.com/about/versions/12/foreground-services?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running?hl=ko&quot;&gt;https://developer.android.com/topic/libraries/architecture/workmanager/advanced/long-running?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/develop/background-work/services/foreground-services?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/develop/background-work/services/foreground-services?hl=ko&lt;/a&gt;&lt;/p&gt;</description>
      <category>Mobile Dev/개발후기</category>
      <category>background</category>
      <category>WorkManager</category>
      <category>백그라운드처리</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/187</guid>
      <comments>https://kimback03.tistory.com/187#entry187comment</comments>
      <pubDate>Thu, 8 Feb 2024 10:59:22 +0900</pubDate>
    </item>
    <item>
      <title>GPS 관련 기능 사용정리 (location manager, fused location provider)</title>
      <link>https://kimback03.tistory.com/186</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 블루스웨터 소프트 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자의 위치정보를 활용하여 기능 구현을 하는것은 이제 흔한 일이 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;진행한 프로젝트를 기반으로 내용을 정리해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최근에 작업에 참여하게된 프로젝트는 골프 관련 앱으로써 사용자의 위치 정보가 굉장히 중요한 요소 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위치 정보를 활용하여 라운딩 정보를 기록하고 모니터링, 분석 하는 앱 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위치 정보를 사용하지 못한다면 앱 기능의 절반은 사용 불가라고 봐도 무방할 정도 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;때문에 다시한번 위치정보에 대한 내용을 정리하는것이 필요했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;GPS의 이해&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPS(&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;Global Positioning System)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;는 미국 국방부에서 개발하여 현재는 군사, 공공, 민간 기업, 일반 등 다양하게 활용되어지고 있습니다. GPS의 연간 유지보수 비용은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;약 7억5천만달러 입니다만 현재는 무료로 사용되어지고 있습니다.  &amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우리의 일상속에서 GPS 의 기능을 찾아보면 이미 많은곳에서 쓰이고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 위치를 기준으로 배달을 시켜 먹거나, 택시를 부르거나, 중고거래를 하거나 굉장히 많은 일들이 이미 GPS 의 혜택을 받고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPS 사용 구현에 있어서 단연 중요한것은 위치의 정확도 입니다. 위치가 정확하지 않다면 해당 개발 자체가 무의미 합니다. 만약 이런경우 기획적으로 방향을 틀어서 개발할수 밖에 없겠습니다. 다행히도 안드로이드에서는 정확한 GPS 활용을 위해 몇가지 방법을 제시합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Location Manager 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Location Manager는 정말 간단하게 현재 위치 정보를 수신받고, 리스너를 통해 이벤트를 핸들링 할수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 권한을 추가해주겠습니다. Manifest에 아래의 위치 정보 관련 권한을 추가 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297651268&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;&amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot; /&amp;gt;
&amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_COARSE_LOCATION&quot;/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;system service 로 location manager를 지정하고 필요한 조건을 구성 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297651268&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
var location: Location? = null

//조건을 지정한다
val criteria = Criteria().apply {
    accuracy = Criteria.ACCURACY_COARSE
    isCostAllowed = false
}

val provider = locationManager.getBestProvider(criteria, false) //조건을 사용하여 위치정보를 획득

try {
    location = locationManager.getLastKnownLocation(provider) //최근 위치 정보를 획득함
    
    //위치정보 이벤트 리스너
    val locListener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
        }

        override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
        }

        override fun onProviderEnabled(provider: String) {
        }

        override fun onProviderDisabled(provider: String) {
        }
    }
    
    location?.let {
        Log.d(&quot;LocationManager&quot;, &quot;=== ${it} ===&quot;)
    } 

	//위치 정보를 주기적으로 업데이트 받는다
    locationManager.requestLocationUpdates(provider, 500L, 1f, locListener)
} catch (e: Exception) {
    Log.e(&quot;locationManager&quot;, e.message ?: &quot;=== Unknown Exception ===&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;이렇게해서 간단하게 위치 정보를 가져올수 있습니다만...&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;더이상 Location Manager는 사용할 필요가 없게되었습니다. 구글에서 Fused Provider 를 사용하라고 권장하고 있기 때문입니다.&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;(Location Manager 의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;내부메서드 들도 deplicated 되었습니다.&lt;/span&gt;)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;Fused Provider 를 이용하면 GPS, NETWORK 등의 복합적인 방법을 통해 좀더 정확한 위치 정보를 획득하고, 낮은 배터리 사용률을 가지며, 최신의 업데이트를 지원하고, geo fencing, mapapi 등의 관련 서비스로의 연계가 쉽고 편리합니다. 이모든 장점을 두고 굳이 Location Manager 를 사용할 이유가 없겠지요  &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;여기서 잠깐 Android 에서 위치정보를 획득하는 방법에 대해서 정리해보겠습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;크게 GPS를 활용하는 방법과 Network (통신사 cell point , wifi ap 등) 를 활용하는 방법이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;위치정보 제공자의 이해&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;GPS Provider&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GNSS(Global Navigation Satellite System) 위성 항법 기술을 활용한 위치 정보를 수신받습니다. 당연히 실내에 있거나 혹은 지하, 산속 등에서는 굉장히 제한적입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Network Provider&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;통신망 제공자나 WIFI AP 등을 통해 위치정보를 계산하는 방법 입니다. 실내에서도 위치정보를 어느정도 파악할수 있습니다. 다만 Cell 타워 간격이나, WIFI ap 의 유무 등으로 인해 변수가 존재합니다. 때문에 안드로이드는 내부에서 지속적으로 사용자의 네트워크 정보를 수시로 가져갑니다. &lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 두가지를 섞어주면 참 좋을텐데... 그래서 나온게 바로 Fused Location Provider 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 최신의 기술이 응집된? Fused Location Provider 를 구현해보겠습니다. 굉장히 간단합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Fused Location Provider를 사용한 사용자의 마지막 위치 가져오기&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;google-play service 에서 제공하는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; background-color: #ffffff; color: #666666; text-align: left;&quot;&gt;play-services-location 를 추가합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297651270&quot; class=&quot;nginx&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;apply plugin: 'com.android.application'

...

dependencies {
    implementation 'com.google.android.gms:play-services-location:21.1.0'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;FusedLocation을 선언하고 onCreate에서 초기화 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297651270&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;private var fusedLocation: FusedLocationProviderClient? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    fusedLocation = LocationServices.getFusedLocationProviderClient(this)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297651270&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;fusedLocationClient.lastLocation
	.addOnSuccessListener { location : Location? -&amp;gt;
    location?.let{
    	Log.d(&quot;&quot;, &quot;=== ${location} ===&quot;)
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;lastLocation에 리스너를 등록하고 위도 경도 등의 정보를 가지고 있는 Location 정보를 전달받게 됩니다. 그런데 왜 Location? 일까요? 특정 아래의 이유에 의해서 null 이 들어올수 있기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #202124; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기기 설정에서 위치가 사용 중지되어 있습니다. 위치를 사용 중지하면 캐시도 지워지므로 이전에 마지막 위치를 가져온 경우에도 결과는&lt;span&gt;&amp;nbsp;&lt;/span&gt;null일 수 있습니다.&lt;/li&gt;
&lt;li&gt;기기에서 위치를 기록한 적이 없습니다. 예를 들면 새 기기이거나 기본 설정으로 복원된 기기일 수 있습니다.&lt;/li&gt;
&lt;li&gt;기기의 Google Play 서비스가 다시 시작되었으며 서비스가 다시 시작된 후 위치를 요청한 활성 통합 위치 정보 제공자 클라이언트가 없습니다. 이러한 상황이 발생하지 않도록 새 클라이언트를 만들고 직접 위치 업데이트를 요청할 수 있습니다. 자세한 내용은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://developer.android.com/training/location/receive-location-updates?hl=ko&quot;&gt;위치 업데이트 받기&lt;/a&gt;를 참고하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;주의할점은 가져온 위치정보값이 반드시 최신의 값은 아닐수 있다는데 있습니다. &amp;nbsp;마지막으로 기록된 위치가 현재 나의 위치라고 볼수는 없기 때문입니다. (현재 이동한 위치가 기록이 안됐거나)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;그래서 정확한 현재위치를 표기하기 위해서는 다른 방법으로 구현해야 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;&lt;b&gt;Fused Location Provider를 사용한&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;현재 위치 가져오기&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #000000; text-align: left;&quot; href=&quot;https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient?hl=ko#getCurrentLocation(int,%20com.google.android.gms.tasks.CancellationToken)&quot;&gt;getCurrentLocation()&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;메서드를 활용하는 것입니다. 최신의 위치를 가져오는 가장 좋은 방법입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #37474f; text-align: left;&quot;&gt;requestLocationUpdates 를 활용하여 위치를 업데이트 하고 가져오는것보다 안정적 입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #37474f; text-align: left;&quot;&gt;requestLocationUpdates를 사용하여 중단시키지 않으면 배터리 등의 소모가 많을수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #37474f; text-align: left;&quot;&gt;&lt;span style=&quot;color: #37474f; text-align: left;&quot;&gt;&lt;b&gt;Fused Location Provider를 사용한&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;위치 업데이트 등록&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1707297651271&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;private var locationCallback: LocationCallback? = null

override fun onCreate(savedInstanceState: Bundle?) {
	//...
    
    locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult?) {
            locationResult ?: return
            for (location in locationResult.locations){
            	Log.d(&quot;locationCallBack&quot;, &quot;=== location: $location ===&quot;)
            
            }
        }
    }
    
    //...
}

override fun onResume() {
    super.onResume()
    startLocationUpdates()
}

private fun startLocationUpdates() {
    fusedLocation.requestLocationUpdates(locationRequest,
            locationCallback,
            Looper.getMainLooper())
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;업데이트 요청을 시작했다면 중지하는 로직도 필요합니다. 화면이 전환되거나, 앱이 백그라운드로 들어갔다면 위치 정보 획득 중단을 요청할수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297651272&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;override fun onPause() {
    super.onPause()
    stopLocationUpdates()
}

private fun stopLocationUpdates() {
    fusedLocationClient.removeLocationUpdates(locationCallback)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297651272&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;override fun onResume() {
    super.onResume()
    if (requestingLocationUpdates) startLocationUpdates()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;화면 전환시 위치정보를 업데이트 하려면 requestingLocationUpdates 를 활용해서 onResume 에서 startLocationUpdates 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 복잡성 때문에 간단한 현재위치 조회의 경우 getCurrentLocation을 통해 가져오라고 권장하고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마무리글&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;사실 위치 정보를 활용한 앱들은 이전부터 꽤 많이 작업했습니다만, 정리를 안해둔 탓에 매번 찾아보고 다시 공부해야 했습니다. Location Manager 와 Fused Location Provider 에 대해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;이번 글을 작성 하며 지식을 정리하는 계기가 되어 기쁜 마음입니다. 이제는 거의 사용되지 않는 Location Manager의 구현을 작성한 이유는 사용되지 않는다면 기존의 히스토리를 알아둘 필요가 있기 때문입니다. 왜 더 좋은지 왜 사용해야 되는지에 대한 이해가 쉽게 되기 때문입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;누군가 참고를 위해 이글을 읽는다면 앞 내용만 보고 Location Manager 를 사용하지 않기를 바랍니다 &lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;문서참고&lt;/span&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/6775257/android-location-providers-gps-or-network-provider&quot;&gt;https://stackoverflow.com/questions/6775257/android-location-providers-gps-or-network-provider&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;&lt;a href=&quot;https://manorgass.tistory.com/82&quot;&gt;https://manorgass.tistory.com/82&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;&lt;a href=&quot;https://developer.android.com/reference/android/location/LocationManager&quot;&gt;https://developer.android.com/reference/android/location/LocationManager&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.google.com/location-context/fused-location-provider?hl=ko&quot;&gt;https://developers.google.com/location-context/fused-location-provider?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/training/location/retrieve-current?hl=ko&quot;&gt;https://developer.android.com/training/location/retrieve-current?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/training/location/request-updates?hl=ko&quot;&gt;https://developer.android.com/training/location/request-updates?hl=ko&lt;/a&gt;&lt;/p&gt;</description>
      <category>Mobile Dev/개발후기</category>
      <category>Android</category>
      <category>IOS</category>
      <category>개발후기</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/186</guid>
      <comments>https://kimback03.tistory.com/186#entry186comment</comments>
      <pubDate>Wed, 7 Feb 2024 21:00:11 +0900</pubDate>
    </item>
    <item>
      <title>BLE(beacon), Objc-swift, mdm 프로젝트 진행</title>
      <link>https://kimback03.tistory.com/185</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;소개&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;안녕하세요. 블루스웨터 소프트입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하며 느낀점과 기술적인 부분을 정리 하고 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;P사의 프로젝트를 진행하다보면 어려운 점은 다름아닌 보안 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;외부로 통하는 경로가 막혀있기 때문에 필요한 lib 등 오픈소스 활용도 어렵습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;신청을 통해 필요한 경로를 오픈해주기는 하지만 오래걸리고 까다롭기도 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개발하는 입장에서는 굉장히 불편한 일이지만 보안이 철저한것은 어찌됬건 환영할일 입니다  (울면서 웃는 나)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그동안 하드웨어와 앱단의 ble 연동을 원하는 프로젝트들이 있었습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 경우도 마찬가지로 ble를 통해 앱과 디바이스(혹은 특정 단말장치)를&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;연동하여 특정 기기를 필터링 하길 원하였습니다. 특별히 어려운 점은 없어 보였습니다만... &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;함정이 존재했습니다 &amp;nbsp;(이후 글에서 설명)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;블루투스 저전력 프로토콜&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;(Bluetooth Low Energy&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;)은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;2010년&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;블루투스 4.0(Bluetooth Smart) 이후부터 채택되어 사용되기 시작습니다. ble 의 큰 특징은 이름에서 알수 있듯이 아주 작은양의 배터리 만으로도 수년간 동작할수 있는 특징이 있습니다. 속도와 데이터의양이 크지 않아 작은 데이터를 주고받는 가전제품, 센서 통신 등에 활용됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;해당 프로젝트에서는 현장에서 사용하는 Ble 기기 (beacon) 들을 필터링 하여(제조업체별) 기기들의 상태 및 모니터링 하는데 목적이 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: start;&quot;&gt;처음에 단순히 스캐닝만 하고 적당히 표시해주면 될것이라 생각했습니다. 문제는 해당 기기의 정확한 스펙이나 정보가 부족하여 일일히 테스트 해보며 코드를 구현하게 되었습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;필수 권한&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;따로 lib를 쓸 필요가 없기에 CoreBluetooth 를 통해 작업 하였습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #222831; text-align: start;&quot;&gt;CoreBluetooth를 사용하기 위해서는 권한관련을 추가해주셔야 합니다.&lt;/span&gt;&lt;span style=&quot;color: #222831; text-align: start;&quot;&gt;&amp;nbsp;iOS 13 이상에서는&amp;nbsp;&lt;/span&gt;NSBluetoothAlwaysUsageDescription&lt;span style=&quot;color: #222831; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;&lt;/span&gt;iOS 12 이하 버전에서는&amp;nbsp;&lt;/span&gt;NSBluetoothPeripheralUsageDescription&lt;span style=&quot;color: #222831; text-align: start;&quot;&gt;를 추가 해야 합니다. (필수)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-04 오후 2.59.31.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkVE2X/btsEBqyeW6E/TNtW6GrsNRVpVKnVhNr27K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkVE2X/btsEBqyeW6E/TNtW6GrsNRVpVKnVhNr27K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkVE2X/btsEBqyeW6E/TNtW6GrsNRVpVKnVhNr27K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkVE2X%2FbtsEBqyeW6E%2FTNtW6GrsNRVpVKnVhNr27K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1160&quot; height=&quot;54&quot; data-filename=&quot;스크린샷 2024-02-04 오후 2.59.31.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-04 오후 2.59.37.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;44&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cro9s2/btsEAlqXtOG/nF0JxiaseAIOtsFsumlRt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cro9s2/btsEAlqXtOG/nF0JxiaseAIOtsFsumlRt1/img.png&quot; data-alt=&quot;잘 모르면 둘다 추가해보자.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cro9s2/btsEAlqXtOG/nF0JxiaseAIOtsFsumlRt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcro9s2%2FbtsEAlqXtOG%2FnF0JxiaseAIOtsFsumlRt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;44&quot; data-filename=&quot;스크린샷 2024-02-04 오후 2.59.37.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;44&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잘 모르면 둘다 추가해보자.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BLE 사전 지식&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;관련을 작업하기 위해서는 선행 지식이 필요합니다. 중앙장치인 Central&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;와&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;주변기기인&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Peripheral 아래의 이미지를 보면 아시겠지만 주변기기는 데이터를 송신하고 중앙에서 받고 처리하는 형태 입니다. 기존의 서버 클라이언트와 비교해서 개념을 이해하면 더 쉬울듯 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-04 오후 3.04.58.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3g8xJ/btsEA1S1zcL/HSSIV5ENd8j1MvIKbtqKNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3g8xJ/btsEA1S1zcL/HSSIV5ENd8j1MvIKbtqKNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3g8xJ/btsEA1S1zcL/HSSIV5ENd8j1MvIKbtqKNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3g8xJ%2FbtsEA1S1zcL%2FHSSIV5ENd8j1MvIKbtqKNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;393&quot; data-filename=&quot;스크린샷 2024-02-04 오후 3.04.58.png&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 클래식 블루투스와 다른 점은 페어링 형태로 특정 타겟과 연동하는 방식이 아닌 ad(광고) 형태로 끊임없이? 데이터를 발송한다는데 있습니다. 이는 ble 방식의 특징으로 발송된 ad 데이터를 중앙기기에서 받아서 처리 하게 되는 방식입니다. 실제로 이전에 했던 프로젝트를 예를 들면 특정 ble 기기 주변에 근접한 중앙기기들에게 쿠폰을 발송, 유저에게 사용을 유도 한다던가 마케팅 적으로 활용할수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개발시 특정 테스트기기를 두고 구현을 진행했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BLE 테스트 구현&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1707297573344&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;var centralManager = CBCentralManager(delegate: self, queue: nil)
var timer: Timer!

@IBAction func onClickBleScan(_ sender: Any) {//버튼을 두고 테스트 스캔을 시작 합니다.

	if(centralManager?.state == .poweredOn){//특정 상태값 확인
    	showLoading()
    
		self.centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:false])
		self.timer = Timer.scheduledTimer(timeInterval: 5,
                                              target: self,
                                              selector: #selector(self.stopBleScan),
                                              userInfo: nil,
                                              repeats: false)

     }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;타이머를 둔 이유는 5초 동안 스캔후 특정 finish 동작을 수행하고 싶어서였습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297573344&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;@objc func stopBleScan(){
    if(centralManager!.isScanning){ //스캐닝 상태 파악
        centralManager!.stopScan() //스캔 중단
    }
    
    if let _ = timer {
        timer.invalidate()
        timer = nil
    }

    hideLoading()

}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297573345&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;extension BleViewController: CBCentralManagerDelegate {
	func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .unknown:
            print(&quot;===블루투스 상태 알수 없음===&quot;)
        case .resetting:
            print(&quot;===블루투스 서비스 리셋===&quot;)
        case .unsupported:
            print(&quot;===기기가 블루투스를 지원하지 않습니다===&quot;)
        case .unauthorized:
            print(&quot;===블루투스 사용 권한 확인 필요===&quot;)
        case .poweredOff:
            print(&quot;===블루투스 비활성 상태===&quot;)
        case .poweredOn:
            print(&quot;===블루투스 활성 상태====&quot;)
        @unknown default:
            print(&quot;===default===&quot;)
        }
    }

	func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
             print(&quot;==== UUID : \(peripheral.identifier.uuidString) RSSI : \(RSSI.intValue) name: \(peripheral.name) ====&quot;)
             print(&quot;=== advertisementData : \(advertisementData) ===&quot;)
	}


}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;블루투스 상태를 확인하고 스캔되는대로 데이터를 확인하여 필요한 작업을 요청할 생각이었습니다. 실제로 블루투스 스캔을 진행하면 주변에 엄청나게 많은 기기들의 신호가 잡히는 걸 볼수 있습니다. 필요한 기기들만 리스팅 되도록 하는게 필수적이었습니다. 보통의 경우 UUID를 키로 삼아서 해당 기기를 컨트롤 하면 되겠다고 생각하기 십상입니다. 주의 해야 할 점은 ios는 특정 블루투스 기기의 실제 uuid를 보여주지 않는데 있습니다. 실제로 스캔해보면 계속 바뀌는 값을 확인할수 있습니다. 기기의 고유한 비콘 id 값을 확인하려면 특별한 방법이 필요합니다. &amp;nbsp;페어링을 하거나(해당없음) 제조사의 모델값을 전달 받아서 활용하는 방법 입니다. 표준값이 존재합니다만 해당 기기 스펙마다 조금씩 다를수 있기 때문에 연동하고자 하는 기기의 정보를 제대로 파악 해야 합니다. &amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 기기가 어떤 업체의 beacon 기기인지를 사전에 확인하는 것이 중요하게습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Ibeacon, eddystone 등 업체도 다양하고 조금씩 특징이 다릅니다. UUID 를 검색해도 의미가 없기에 고유값으로 활용할 beacon Id 값을 찾아야 했습니다. 그냥 보이지는 않고 내부의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;manufacturer data 를 규격 대로 해석해야 볼수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;예를 들어 &amp;hellip;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;0059 355 asdfb234234 sdf fffff 0000&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이런식의 코드를 비트연산으로 디코딩 하여야 확인할수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;앞 자리는 회사 id, 그다음은 beacon id , voltage, 등등 어떤 정보인지는 기기마다 조금씩 다릅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297573345&quot; class=&quot;stata&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;let advData = AdvertisementData.init(advertisementData)
        var beaconName = advData.localName ?? &quot;&quot;
        if let mData = advData.manufacturerData{
        	 //corp Id
            let manufactureID = UInt16(mData[0]) + UInt16(mData[1]) &amp;lt;&amp;lt; 8
            var mId = String(format: &quot;%04X&quot;, manufactureID)
            print(mId) //-&amp;gt;ex 000D
        
        	//beacon Id parse
            mData.forEach { mInt in
                beaconId += String(format: &quot;%02X&quot;, UInt16(mInt))
            }
            print(&quot;=== beaconId: \(beaconId) ===&quot;)
        
        }
        
        //...
        
 }&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297573377&quot; class=&quot;stylus&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;//특정 장치만 리스팅 하기위함.
if(beaconId.contains(filterPrefix)){
	print(&quot;=== beaconId : \(beaconId) ===&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 긴 과정을 통해 beacon id 값으로 특정 기기만 검색하고 서버로 데이터를 전달할수 있게 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Objc-Swift 혼용&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 프로젝트는 조금 특이한 구조로 이루어져 있습니다. &amp;nbsp;Objc로 된 소스와 Swift 가 같이 혼재된 형태 입니다. 더 특이한 것은 AppDelegate 단은 Objective-c, 이후에 화면단은 Swift 로 되어있습니다.  굉장히 정신없는 구조라서 까닥하다가는 뭘하러 가는건지 길을 잃어 버리기 십상입니다. Objective-c 와 Swift 를 같이 쓰기 위해서는 특별한 셋팅이 필요합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래와 같이 Project -&amp;gt; Build Setting -&amp;gt; Swift Compiler -&amp;gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;Objective-C Bridging Header&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 설정해주셔야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-04 오후 4.05.37.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cebVXS/btsExI1G3pq/8wFw6b3qE1au3mIrgXxPMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cebVXS/btsExI1G3pq/8wFw6b3qE1au3mIrgXxPMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cebVXS/btsExI1G3pq/8wFw6b3qE1au3mIrgXxPMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcebVXS%2FbtsExI1G3pq%2F8wFw6b3qE1au3mIrgXxPMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1548&quot; height=&quot;768&quot; data-filename=&quot;스크린샷 2024-02-04 오후 4.05.37.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;지정한 경로에 해당 브릿지 파일을 만들어 주고 필요한 Objc 클래스를 import 하면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-04 오후 4.09.38.png&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbu8Qf/btsEytQwM1F/6CUJnHK0HAOEQYOPWekpJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbu8Qf/btsEytQwM1F/6CUJnHK0HAOEQYOPWekpJK/img.png&quot; data-alt=&quot;bridge 파일을 생성한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbu8Qf/btsEytQwM1F/6CUJnHK0HAOEQYOPWekpJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbbu8Qf%2FbtsEytQwM1F%2F6CUJnHK0HAOEQYOPWekpJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;70&quot; data-filename=&quot;스크린샷 2024-02-04 오후 4.09.38.png&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bridge 파일을 생성한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297573378&quot; class=&quot;clean&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;#import &quot;AppDelegate.h&quot;
#import &quot;DeviceUtil.h&quot;
#import &quot;NSData+AES128.h&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 프로젝트의 특이한 점은 AppDelegate 가 Objective-C 로 되어있다는 점 입니다. 시작점이 Objective-C 라는 이야기는 Objective-C 에서 Swift 를 호출하는 방법도 작성해야 한다는 말입니다.  필요한 클래스에 @objc 어노테이션을 활용하면 간단합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297573378&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;@objc 
class TextUtil: NSObject {
   @objc static let shared = TextUtil()
   
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 swift를 사용할 objc 클래스로 이동하고 import , 그리고 사용하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297573378&quot; class=&quot;clean&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;#import &quot;Pooo-Swift.h&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297573378&quot; class=&quot;scheme&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;[[TextUtil shared] file:@&quot;AppDelegate&quot; msg:@&quot;=== 여기서 이동 시작 ===&quot;];&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MDM 관련&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 프로젝트에서는 이미 MDM 관련 기능을 제공하고 있기에 최소한의 사전 지식이 필요했습니다. MDM은 수 많은 기기들을 관리 하는 일종의 보안 솔루션 (보안, 감시, 제한 등) 으로 일반적으로 사용할수 없는 위험한? 기능들을 제공하고 있습니다. 보안이 중요한 업계에서는 이미 많이들 사용하는 솔루션 입니다. MDM의 기조는 사용자를 억압하고 제한하는 것입니다. 때문에 사생활 침해 등의 우려는 덤으로 따라오는 옵션이었습니다. 때문에 업무용 폰과 태블릿을 따로 지급하고 해당 기기에만 MDM 을 설치하는 것이 표준이 되고 있습니다만(이번 프로젝트는 그렇지 않습니다 ) 너무 불편했기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;EMM : Enterprise Mobility Management 이라는 새로운 솔루션이 등장합니다. 좀더 세세하게 특정 앱이나, 서비스만을 통제할수 있고, 사생활 침해 등은 최소화 하는 솔루션입니다. 이번 프로젝트의 경우&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;위치 정보를 기준으로 카메라를 막고, 스크린샷을 막는등 필요한 제한기능을 사용하고 있었습니다. Apple 기기를 MDM 관리 하기 위해서는 개발시 상당히 긴 시간이 필요한것으로 보였습니다. Apple 에서 제공하는 공식 MDM 서비스는 따로 없고 아래의 서비스 등록한뒤 타사의 MDM 솔루션을 사용 해야 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;애플 등록&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; color: #000000; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple DEP(Device Enrollment Program)&lt;/li&gt;
&lt;li&gt;APPLE MDM (ABM(Apple Business Manager)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;타사 솔루션&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- VMware AirWatch&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- IBM MaaS360&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- Microsoft Intune&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- Citrix XenMobile&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- Cisco Meraki System Manager&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- Jamf&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- Hexnode MDM&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: justify;&quot;&gt;- SimpleMDM&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마무리글&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사내배포 앱을 제작하는 특수성을 가진 프로젝트로써 많은 보안 과제들을 처리해야하는 업무였습니다. BLE, 엔터프라이즈 앱배포 등 관련 내용을 다시한번 정리하고 MDM 같은 특수성을 가진 연동 처리 또한 새로운 지식을 쌓는 계기가 되었습니다. 정말이지 IT 지식은 끝도 없고 양도 방대함을 다시금 깨닿는 계기 였습니다. &amp;zwj; &lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;문서참고&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://support.apple.com/ko-kr/guide/deployment/depc0aadd3fe/web&quot;&gt;https://support.apple.com/ko-kr/guide/deployment/depc0aadd3fe/web&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.sharedit.co.kr/posts/8235#&quot;&gt;https://www.sharedit.co.kr/posts/8235#&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/12524871/corebluetooth-how-to-get-a-unique-uuid?rq=3&quot;&gt;https://stackoverflow.com/questions/12524871/corebluetooth-how-to-get-a-unique-uuid?rq=3&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.bluetooth.com/specifications/assigned-numbers/&quot;&gt;https://www.bluetooth.com/specifications/assigned-numbers/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothOverview/CoreBluetoothOverview.html&quot;&gt;https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothOverview/CoreBluetoothOverview.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>Mobile Dev/개발후기</category>
      <category>ble</category>
      <category>IOS</category>
      <category>개발후기</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/185</guid>
      <comments>https://kimback03.tistory.com/185#entry185comment</comments>
      <pubDate>Wed, 7 Feb 2024 20:00:05 +0900</pubDate>
    </item>
    <item>
      <title>MVVM-C 패턴을 활용한 앱 개발 후기</title>
      <link>https://kimback03.tistory.com/184</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 블루스웨터 소프트 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클린 아키텍처, 클린 코드(&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://m.yes24.com/Goods/Detail/11681152&quot;&gt;clean code:&lt;span style=&quot;background-color: #ffffff; color: #666666; text-align: left;&quot;&gt;Robert C. Martin&lt;/span&gt;&lt;/a&gt;) 등은 이제 주변에서 흔하게 접하는 단어가 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MVC, MVP, MVVM... 각종기법이 사용되고 있습니다. 장기적으로 유지보수측면서 환영할만한 일이지만&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 기법들이 무분별하게 사용되어진 프로젝트들을 보면 (모든 기법이 뒤섞인) 유지보수측면에서 정말 지옥이 아닐수 없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 MVVM-C 라고 하는 패턴을 사용한 작업 후기를 공유 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MVVM 패턴은 매우 인기있는 클린아키텍처 접근 방법 입니다. 이 패턴에 Coordinator 라는 개념을 추가 한것이 MVVM-C 패턴 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;왠 코디네이터 입니까?&quot;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코디네이터는 일종의 목적지 가이드 라고 이해하면 쉽게 접근할수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코디는 우리가 어디로 가야할지, 무엇이 필요한지를 아는자 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297487775&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;func goToLogin() { 
    let vc = LoginViewController.instantiate(from: authStoryboard) //목적지를 정하고
    let vm = LoginViewModel() //필요한 정보를
    vm.apiClient = authApi 
    vm.authCoordinator = self
     vc.viewModel = vm //전달합니다
    navigationController.setViewControllers([vc], animated: true ) //목적지로 이동합니다
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하나의 코디네이터 클래스에는 위와 같은 여러개의 이동정보가 존재합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-01 오후 2.36.42.png&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3DhgV/btsEx8lxgXs/V6B5Jg2NM1ns6KTks13Dy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3DhgV/btsEx8lxgXs/V6B5Jg2NM1ns6KTks13Dy1/img.png&quot; data-alt=&quot;coordinator의 설계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3DhgV/btsEx8lxgXs/V6B5Jg2NM1ns6KTks13Dy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3DhgV%2FbtsEx8lxgXs%2FV6B5Jg2NM1ns6KTks13Dy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;343&quot; data-filename=&quot;스크린샷 2024-02-01 오후 2.36.42.png&quot; data-origin-width=&quot;668&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;coordinator의 설계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AppCoordinator 라는 공통의 부모 코디가 있고 그 밑에는 화면별로 코디가 존재하게 됩니다. 화면에 따른 Coordinator, ViewModel, ViewController, View... 등이 존재하는 것이죠.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297487775&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;protocol Coordinator {
    var parentCoordinator: Coordinator? { get set }
    var children: [Coordinator] { get set }
    var navigationController : UINavigationController { get set }
    
    func start()
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297487776&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;class AppCoordinator: Coordinator {
var parentCoordinator: Coordinator?
var children: [Coordinator] = []
var navigationController: UINavigationController    

init(navCon : UINavigationController) {
	self.navigationController = navCon
}    

func start() {
    print(&quot;===앱의 시작점===&quot;)
}}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;예를 들면 AppDelegate는 아래와 같이 만들어집니다. root navigation 을 만들고 appCoordi 에게 전달 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297487776&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;class AppDelegate: UIResponder, UIApplicationDelegate {
   var window: UIWindow?
   var appCoordinator : AppCoordinator?

   func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&amp;gt; Bool {
       
        window = UIWindow(frame: UIScreen.main.bounds)
        let navigationCon = UINavigationController.init()
        appCoordinator = AppCoordinator(navigationController: navigationCon)
        appCoordinator!.start()
        window!.rootViewController = navigationCon
        window!.makeKeyAndVisible()
        return true
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Coordinator의 핵심은 전달받은 navigator로 화면 처리를 운용하는데 목적이 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297487777&quot; class=&quot;autoit&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt; func start() {
	goToLoginPage()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AppCoordinator에 소스를 추가 합니다. 화면은 띄워봐야 하니까요.&lt;/p&gt;
&lt;pre id=&quot;code_1707297487777&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;let storyboard = UIStoryboard.init(name: &quot;Main&quot;, bundle: .main) //스토리보드사용시

func goToLoginPage(){
     let loginViewController = storyboard.instantiateViewController(withIdentifier: &quot;LoginViewController&quot;) as! LoginViewController //화면을 구성합니다.
     let loginViewModel = LoginViewModel.init() //뷰모델을 생성하고
     loginViewModel.appCoordinator = self //뷰 모델이 코디를 알아야 호출하겠죠?
     loginViewController.viewModel = loginViewModel //viewController는 ViewModel을 알아야합니다.
     navigationController.pushViewController(loginViewController, animated: true) //마지막은 push하면 됩니다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드를 보면 아시겠지만 코디가 하는 일은 그저 화면을 만들고 그안에 필요한 내용을 구성해주는 역할이 다 입니다. 클린 아키텍처의 핵심은 규칙을 잘 지키고, 할일만 하고, 서로를 잘 몰라야 합니다. UI는 ViewModel을 모르고, ViewModel은 Repository, Service를 모릅니다. (모른다는건 생성하지 않는다는 종속되지 않는다는 의미) DI를 통해 전달받고 그저 사용할뿐입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;뷰모델도 살펴 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297487777&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;class LoginViewModel {
    weak var coordinator : AppCoordinator!
    
    func goToLogin(){
        appCoordinator.goToLoginPage()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정말 단순한 예제 입니다 &lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;뷰컨트롤러도 확인해보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297487778&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;class LoginViewController : UIViewController {
    var viewModel : LoginViewModel!
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    @IBAction func goToLoginTapped(_ sender: Any) {
        viewModel.goToLogin()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;뷰컨트롤러도 사실상 하는일이 없습니다. 비지니스 로직에는 전혀 관혀하지 않습니다. 유저 와 View 간의 상호작용하고 ViewModel 로 모든 로직을 위임합니다. ViewModel에서는 상황에 맞게 Service, Repository 를 운용하고 최종적으로 Coordinator를 통해 화면을 이동시켜줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;MVVM 의 개념을 알고있다면 Coordinator만 추가 해주면 됩니다. Coordinator를 추가함으로써 복잡한 화면 이동처리를 위임하고 비즈니스 로직에 집중할수 있습니다. 이것이 MVVM-C 활용의 전부 입니다.  &lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;참고문서&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/understanding-mvvm-c-budhabhooshan-patil/&quot;&gt;https://www.linkedin.com/pulse/understanding-mvvm-c-budhabhooshan-patil/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/nerd-for-tech/mvvm-coordinators-ios-architecture-tutorial-fb27eaa36470&quot;&gt;https://medium.com/nerd-for-tech/mvvm-coordinators-ios-architecture-tutorial-fb27eaa36470&lt;/a&gt;&lt;/p&gt;</description>
      <category>Mobile Dev/개발후기</category>
      <category>IOS</category>
      <category>개발후기</category>
      <category>클린아키텍처</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/184</guid>
      <comments>https://kimback03.tistory.com/184#entry184comment</comments>
      <pubDate>Wed, 7 Feb 2024 18:19:04 +0900</pubDate>
    </item>
    <item>
      <title>SwiftUI +Preview 를 활용하여 UI 작성 방법</title>
      <link>https://kimback03.tistory.com/183</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 블루스웨터소프트 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하며 재미있는 방법으로? UI를 구성한적이 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;꽤나 유용한 방법이어서 해당 내용을 공유 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 프로젝트에 SwiftUI 의 Preview 를 적용하여 개발하는 방식입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 방법을 활용하면 Storyboard, Swift등은 그대로 쓰면서, UI 개발시 바로 바로 Preview 를 보면서 개발이 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 되면 UI 구성을 확인하기 위해 매번 빌드 해서 확인하고 빌드해서 확인하는 수고로움이 덜어질 것입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;필요한 부분만 컴파일 되어 보여지므로 속도 또한 빠릅니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;방법은 정말 간단합니다. 프로젝트에서 Swift 파일을 하나 생성합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 기존의 방법대로 UIkit 등 필요한 라이브러리를 import 하시고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;맨 아래 부분에 &quot;#if DEBUG&quot; 전처리문으로 시작하는 아래의 소스코드를 넣어주겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297123522&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#if DEBUG
import SwiftUI

struct HybridSwiftViewController_ViewPresentable: UIViewControllerRepresentable {
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print(&quot;=== HybridSwiftViewController_ViewPresentable updateUIViewController ==&quot;)
    }
    
    func makeUIViewController(context: Context) -&amp;gt; some UIViewController {
        HybridSwiftUIViewController()
    }
    
}

struct HybridViewController_PreviewProvider : PreviewProvider {
    static var previews: some View {
        HybridSwiftViewController_ViewPresentable()
            //.ignoresSafeArea()
    }
}
#endif&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;소스 코드를 보면 아시겠지만 정말 별거 없이 SwiftUI의 Preview를 활용하기 위해 PreviewProvider 프로토콜을 구현 해주면 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;HybridSwiftViewController&quot; 부분은 자신의 클래스를 따라서 작성해줍니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 작성하자마자 오른쪽에 Preview가 등장하고 왠 화면이 보이게 됩니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;짐작하신대로 이부분이 Preview 화면 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297123522&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;swift&quot;&gt;&lt;code&gt;private lazy var topContainer = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 300))
private lazy var titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

override func viewDidLoad() {
    print(&quot;=== HybridSwiftUIViewController viewDidLoad ==&quot;)

    titleLabel.text = &quot;안녕하세요&quot;
    titleLabel.backgroundColor = .white
    topContainer.backgroundColor = .blue
    topContainer.addSubview(titleLabel)
    self.view.addSubview(topContainer)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;간단하게 작성한뒤 화면 출력을 확인해 봅니다. 값도 변경해보면서 테스트 해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-02-01 오후 1.59.51.png&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;1860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Xb78b/btsEx6acM97/JK3R5trfEU1Snu3waqkKSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Xb78b/btsEx6acM97/JK3R5trfEU1Snu3waqkKSk/img.png&quot; data-alt=&quot;preview 화면을 보며 개발한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Xb78b/btsEx6acM97/JK3R5trfEU1Snu3waqkKSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXb78b%2FbtsEx6acM97%2FJK3R5trfEU1Snu3waqkKSk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;511&quot; data-filename=&quot;스크린샷 2024-02-01 오후 1.59.51.png&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;1860&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;preview 화면을 보며 개발한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;만약 화면이 안보인다면 Xcode 오른쪽 상단에 해당 버튼을 확인하세요.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Canvas가 체크되어있는지 확인하고 단축키도 사용해봅니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #f89009;&quot;&gt;&lt;b&gt;Canvas 표시 단축키 : Cmd+Opt+Enter&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT1cZq/btsEyRjA1NI/FemLvhSzpadLimrIVaGtVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT1cZq/btsEyRjA1NI/FemLvhSzpadLimrIVaGtVk/img.png&quot; style=&quot;width: 16.632799%; margin-right: 10px;&quot; data-widthpercent=&quot;16.83&quot; data-filename=&quot;스크린샷 2024-02-01 오후 2.02.40.png&quot; data-origin-height=&quot;84&quot; data-origin-width=&quot;78&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT1cZq/btsEyRjA1NI/FemLvhSzpadLimrIVaGtVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT1cZq%2FbtsEyRjA1NI%2FFemLvhSzpadLimrIVaGtVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;78&quot; height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9bIxW/btsEBVkr1Jy/GUU22bCY63jbbSIdKRUyH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9bIxW/btsEBVkr1Jy/GUU22bCY63jbbSIdKRUyH1/img.png&quot; style=&quot;width: 82.20441%;&quot; data-widthpercent=&quot;83.17&quot; data-filename=&quot;스크린샷 2024-02-01 오후 2.02.45.png&quot; data-origin-height=&quot;112&quot; data-origin-width=&quot;514&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9bIxW/btsEBVkr1Jy/GUU22bCY63jbbSIdKRUyH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9bIxW%2FbtsEBVkr1Jy%2FGUU22bCY63jbbSIdKRUyH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;514&quot; height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 활용하면 매번 컴파일 할 필요 없이 다이렉트로 화면을 보며 작업 할수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 storyboard로 된 프로젝트나 view를 매번 커스텀하여 사용하셨다면 이번 기회에 SwiftUI 를 활용하면 좋을것 같습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;마무리글&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;실제로 프로젝트를 진행하며 앱개발에 활용해보니 굉장히 편리했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 storyboard나 view를 코딩 작업하는 경우 매번 빌드-런-빌드-런의 과정을 통해 변경된 UI를 확인하는 것은 꽤나 번거롭고 시간이 많이 소요되는 일이었습니다. 최근에 진행한 flutter 개발에서도 핫픽스 기능을 통해 바로 바로 변경된 UI를 보며 작업할수 있었습니다. 앞으로 UI 작업시에 꽤나 유용할것 같습니다.&lt;/p&gt;</description>
      <category>Mobile Dev/개발후기</category>
      <category>IOS</category>
      <category>개발후기</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/183</guid>
      <comments>https://kimback03.tistory.com/183#entry183comment</comments>
      <pubDate>Wed, 7 Feb 2024 18:17:45 +0900</pubDate>
    </item>
    <item>
      <title>Flutter와 iOS 그리고 extension 간에 데이터를 공유 후기</title>
      <link>https://kimback03.tistory.com/182</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 블루스웨터소프트 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Flutter를 사용해보니 편리함과 동시에 앱의 복잡도가 증가했습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개발 툴사용만 봐도 그렇습니다. 저는 vscode를 이용해서 flutter 코드를 작성합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;안드로이드는 android studio, ios 는 xcode 에서 따로 작업합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;때문에 리소스도 상당히 많이 잡아먹는 작업입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼에도 Flutter의 많은 장점들 때문에 최근 가장 트랜디한 개발 언어가 되었겠죠.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;개발을 하며 필수적으로 native 소스단에 접근하게 되는데요. 이럴때마다&amp;nbsp;선택지가 많다보니 어디에 핵심 로직을 둘것인가?하는 문제가 생기게 됩니다. 오늘은 개발시 있었던 일화와 간단한 값을 (읽기, 쓰기) 테스트 해보면서 좀더 이야기 해보겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 제 상황을 공유하자면...&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Flutter&lt;/li&gt;
&lt;li&gt;ios&lt;/li&gt;
&lt;li&gt;extension&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 크게 3가지의 파트로 나뉘어 집니다. sms 문자를 컨트롤 해야하는 상황이라 extension을 필수로 사용하게 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그러면서 자연스럽게 3 파트의 데이터 공유에 대해서 생각해보게 되었습니다. 데이터를 어디서 저장하고 어디서 활용해야 하지?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또 어떤 방식으로 저장을 할까? (shared preference, UserDefaults, realm 등)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;Flutter의 Shared Preference 플러그인의 사용&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 플러그인을 사용하면 android의 shared preferences 와 ios의 userDefault 와 유사하게 활용할수 있습니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;간단한 데이터(iOS 및 macOS의 NSUserDefaults, Android의 SharedPreferences 등)를 위한 플랫폼별 영구 저장소를 래핑합니다&lt;br /&gt;&lt;br /&gt;Supported data types are int, double, bool, String and List&amp;lt;String&amp;gt;.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0175c2; text-align: start;&quot; href=&quot;https://flutter.dev/docs/development/platform-integration/platform-channels&quot;&gt;pubspec.yaml 파일에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/a&gt;shared_preferences&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a style=&quot;color: #0175c2; text-align: start;&quot; href=&quot;https://flutter.dev/docs/development/platform-integration/platform-channels&quot;&gt;종속성으로&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;추가하십시오&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a; text-align: start;&quot;&gt;.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297020266&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;shared_preferences: ^2.2.2&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 쓰기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297020266&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(&quot;userId&quot;, &quot;kakao12345&quot;);
await prefs.setString(&quot;userLoginType&quot;, &quot;kakao&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정말 간단하죠? 읽기도 확인해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 읽기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1707297020267&quot; class=&quot;less&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;prefs.getString(&quot;userId&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;읽기도 마찬가지로 너무 쉽습니다. (당연) &amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;쓰기(set)에서 await를 통한 비동기처리를 해주어야 합니다. 쓰기(set)시 비동기 처리가 필요한 이유는 쓰기 작업시 dart의 캐시를 활용하기 때문이라고 합니다. 때문에 가시성의 문제가 생길수 있습니다. (SharedPreferences.getInstance() = 싱글턴) 최신의 데이터를 보장해야 한다면 reload()를 통해서 캐시를 갱신해야 합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 쓴 데이터를 제 경우 ios 앱에서 활용했습니다. 과연 ios에서 잘 확인이 될까요?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;네 당연히 됩니다. 대신 prefix 값을 붙혀주셔야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297020267&quot; class=&quot;css&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;UserDefaults.standard.object(forKey:&quot;flutter.userId&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이런식으로 말입니다. &quot;flutter.&quot; prefix가 붙는 이유에 대해서도 문서 잘 설명되어 있습니다.&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;By default, the SharedPreferences plugin will only read (and write) preferences that begin with the prefix&amp;nbsp;&lt;br /&gt;flutter.. This is all handled internally by the plugin and does not require manually adding this prefix.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 이값을 extension 까지 끌고 갈수 있을까요? ios과 extension에서 데이터 공유를 위해 특별한 방법을 사용해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;바로 AppGroup을 등록해야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwUPxk/btsEx9SjbZk/LykxTkebN643dNwrPJTKGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwUPxk/btsEx9SjbZk/LykxTkebN643dNwrPJTKGk/img.png&quot; style=&quot;width: 29.821956%; margin-right: 10px;&quot; width=&quot;235&quot; height=&quot;174&quot; data-widthpercent=&quot;30.17&quot; data-filename=&quot;스크린샷 2024-01-30 오후 12.01.11.png&quot; data-origin-height=&quot;1026&quot; data-origin-width=&quot;1388&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwUPxk/btsEx9SjbZk/LykxTkebN643dNwrPJTKGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwUPxk%2FbtsEx9SjbZk%2FLykxTkebN643dNwrPJTKGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1388&quot; height=&quot;1026&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QUhfB/btsEz9jISQr/3zoWbyHty7hL5FZNQ53k5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QUhfB/btsEz9jISQr/3zoWbyHty7hL5FZNQ53k5K/img.png&quot; style=&quot;width: 69.015253%;&quot; width=&quot;445&quot; height=&quot;142&quot; data-widthpercent=&quot;69.83&quot; data-filename=&quot;스크린샷 2024-01-30 오후 12.02.10.png&quot; data-origin-height=&quot;260&quot; data-origin-width=&quot;814&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QUhfB/btsEz9jISQr/3zoWbyHty7hL5FZNQ53k5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQUhfB%2FbtsEz9jISQr%2F3zoWbyHty7hL5FZNQ53k5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;814&quot; height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;capabillities의 App Group을 등록하세요.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앱 그룹을 생성하고 (apple dev) 필요한 extension도 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d5Tdkw/btsEBYg9Dyq/csMrqmXTqdryMRw1JwilMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d5Tdkw/btsEBYg9Dyq/csMrqmXTqdryMRw1JwilMK/img.png&quot; style=&quot;width: 53.546608%; margin-right: 10px;&quot; width=&quot;482&quot; height=&quot;351&quot; data-widthpercent=&quot;54.18&quot; data-filename=&quot;스크린샷 2024-01-30 오후 12.04.05.png&quot; data-origin-height=&quot;1068&quot; data-origin-width=&quot;1468&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d5Tdkw/btsEBYg9Dyq/csMrqmXTqdryMRw1JwilMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd5Tdkw%2FbtsEBYg9Dyq%2FcsMrqmXTqdryMRw1JwilMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1468&quot; height=&quot;1068&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nL6m9/btsEBZ1qMwI/GNwcmuRHHkWPPKlnvX7Ev1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nL6m9/btsEBZ1qMwI/GNwcmuRHHkWPPKlnvX7Ev1/img.png&quot; style=&quot;width: 45.290601%;&quot; data-widthpercent=&quot;45.82&quot; data-filename=&quot;스크린샷 2024-01-30 오후 12.03.49.png&quot; data-origin-height=&quot;246&quot; data-origin-width=&quot;286&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nL6m9/btsEBZ1qMwI/GNwcmuRHHkWPPKlnvX7Ev1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnL6m9%2FbtsEBZ1qMwI%2FGNwcmuRHHkWPPKlnvX7Ev1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;286&quot; height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;데이터 공유를 위해서는 각각의 타겟별을 같은 AppGroup에 속하게 등록해줘야 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그리고 나서 데이터를 사용해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1707297020268&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;extension UserDefaults {
    static var shared: UserDefaults {
        let appGroupId = &quot;group.ZNY6123123.com.blueswt.abc.smsfilter&quot;
        return UserDefaults(suiteName: appGroupId)!
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;편의성을 위해 UserDefaults 확장함수를 만들고 앱 그룹속성을 지정해줍니다. appGroupId는 당연히 등록할때 지정한 id 입니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용방법은 동일합니다. 유틸클래스를 만들었다면 타겟 멤버쉽에 체크하는것을 잊으면 안됩니다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-01-30 오후 3.27.40.png&quot; data-origin-width=&quot;259&quot; data-origin-height=&quot;106&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsQLK2/btsEBmWUCup/FwUXnPxRtZY4sp90YQM9mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsQLK2/btsEBmWUCup/FwUXnPxRtZY4sp90YQM9mK/img.png&quot; data-alt=&quot;매번 깜빡하게되는 것&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsQLK2/btsEBmWUCup/FwUXnPxRtZY4sp90YQM9mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsQLK2%2FbtsEBmWUCup%2FFwUXnPxRtZY4sp90YQM9mK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;259&quot; height=&quot;106&quot; data-filename=&quot;스크린샷 2024-01-30 오후 3.27.40.png&quot; data-origin-width=&quot;259&quot; data-origin-height=&quot;106&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;매번 깜빡하게되는 것&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1707297020268&quot; class=&quot;reasonml&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;UserDefaults.shared.set(&quot;hello world&quot;, forKey: &quot;intro&quot;)
UserDefaults.shared.synchronize()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 문장을 set 하고 extension에서 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297020269&quot; class=&quot;gauss&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;let intro: String? = UserDefaults.shared.string(forKey: &quot;intro&quot;)
print(&quot;=== \(intro) ===&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;너무나 간단하게 값을 공유하게 되었습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;SMS Filter Extension 의 특수성&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 저의 경우 복병이 있었습니다. 제가 사용했던 MessageFilterExtension의 경우 아무리 이렇게 저렇게 해봐도 데이터 공유가 안되는 문제가 있었습니다. 너무 황당해서 realm도 써보고 모든 시도를 한 뒤에 문서를 다시 보기 시작했습니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;For privacy reasons, the system handles all communication with your associated server; your Message Filter app extension can&amp;rsquo;t access the network directly. Your app extension also can&amp;rsquo;t write data to containers shared with the containing app.&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아 안된다고 하네요...  애초에 되는게 이상했던것입니다. 즉 ios 에서 쓴 데이터를 읽기만 가능하고 extension 에서는 쓰기 할수 없다는 말이었습니다. (보안상 문제) &amp;nbsp;혹시라도 해당 extension 을 사용하시는 분들에게 참고가 되면 좋겠습니다.&lt;/p&gt;
&lt;h1 style=&quot;color: #f5f5f7; text-align: left;&quot; data-v-53c3ef2a=&quot;&quot;&gt;&lt;span style=&quot;background-color: #dddddd; color: #000000;&quot;&gt;&lt;b&gt;SMS and MMS Message Filtering&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f89009; color: #ffffff;&quot;&gt;= 데이터 공유 불가&lt;/span&gt;&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;꼭 확인 하시고 몇날 몇일 날리지 마시길 바랍니다 &lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 이런 저런 방법을 생각하다가 flutter channel을 통해서 데이터(userId) 를 전달하고 ios에서 받아서 shared 처리한후 extension에서 읽기 하는 방법으로 최종 결정 내리게 되었습니다. 어떤게 정답이라고 할수는 없지만 제 경우 소스코드의 일관성이 중요했습니다. 어떤값은 flutter에서 처리하고 어떤값은 ios에서 처리하고 유지보수 측면에서도 꼭 필요한 기준이었습니다. (현장에서는 지켜지지 않지만 )&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;기타 lib 활용&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이어서 ios 와 extension 사이의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;realm&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;사용방법에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;realm 이 atras device sdks 로 바뀐 모양입니다. 설치 방법 확인후 spm, cocoapod 어떤 방법이든 상관없이 편한 방식으로 설치를 해줍니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.mongodb.com/docs/realm/sdk/swift/install/&quot;&gt;https://www.mongodb.com/docs/realm/sdk/swift/install/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;마찬가지로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;appGroup의 id를 사용하여 realm을 인스턴스를 사용&lt;/b&gt;&lt;/span&gt;하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1707297020276&quot; class=&quot;routeros&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;var config = Realm.Configuration()
config.fileURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: &quot;appGroup.com.bluswt.abc.myapp&quot;)!.appendingPathComponent(&quot;default.realm&quot;)
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297020276&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;class PatternData: Object {
    @Persisted(PrimaryKey: true)
    var number: Int // primary key로 지정
    @Persisted
    var blackWord: String
    @Persisted
    var date: Date

    convenience init(number: Int) {
        self.init()
        self.number = number
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297020277&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;func getPatternDataAll() -&amp;gt; Results&amp;lt;PatternData&amp;gt;{
  let objects = realm.objects(PatternData.self)
  return objects
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1707297020277&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt; func addPatternData(data: PatternData){
        try! realm.write {
            realm.add(data)
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그 뒤에 읽고 쓰고 데이터를 자유롭게 공유 할수 있습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앱 개발을 하며 생각하게 되는 데이터 저장 방식에 대해서 이야기 하였습니다. 일반적인 상황이 아니기 때문에 Flutter, ios, (android) extension 등 파트 별로 어떻게 데이터를 전달할지 굉장히 난해한 경우가 생깁니다. 기준을 잘 잡고 데이터가 어디서 어떻게 흘러가는지 잘 파악하고 구조를 잡는게 중요할것 같습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #000000;&quot;&gt;*** sms filter extension 에서는 절대 데이터 저장을 하지마세요.  &lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;참고문서&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.mongodb.com/docs/realm/sdk/?_ga=2.205025232.1589213261.1706596786-1205906347.1705670983&quot;&gt;https://www.mongodb.com/docs/realm/sdk/?_ga=2.205025232.1589213261.1706596786-1205906347.1705670983&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.apple.com/documentation/sms_and_call_reporting/sms_and_mms_message_filtering/&quot;&gt;https://developer.apple.com/documentation/sms_and_call_reporting/sms_and_mms_message_filtering/&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/57608338/access-flutter-sharedpreferences-in-swift&quot;&gt;https://stackoverflow.com/questions/57608338/access-flutter-sharedpreferences-in-swift&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pub.dev/packages/shared_preferences&quot;&gt;https://pub.dev/packages/shared_preferences&lt;/a&gt;&lt;/p&gt;</description>
      <category>Mobile Dev/개발후기</category>
      <category>flutter</category>
      <category>flutter후기</category>
      <category>IOS</category>
      <category>플루터</category>
      <author>코드와트</author>
      <guid isPermaLink="true">https://kimback03.tistory.com/182</guid>
      <comments>https://kimback03.tistory.com/182#entry182comment</comments>
      <pubDate>Wed, 7 Feb 2024 18:11:07 +0900</pubDate>
    </item>
  </channel>
</rss>