<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ggg9_.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 13 Aug 2025 14:42:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ggg9_.log</title>
            <url>https://velog.velcdn.com/images/ggg9_/profile/35bc705f-57d8-41ef-8cec-635f4e4086ff/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ggg9_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ggg9_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[AWS Summit Seoul 2025]]></title>
            <link>https://velog.io/@ggg9_/AWS-Summit-Seoul-2025</link>
            <guid>https://velog.io/@ggg9_/AWS-Summit-Seoul-2025</guid>
            <pubDate>Wed, 13 Aug 2025 14:42:29 GMT</pubDate>
            <description><![CDATA[<h1 id="기조연설-simplexity-원칙">기조연설: Simplexity 원칙</h1>
<p>아래는 AWS Summit Seoul 2025에서 아마존 CTO Dr. Werner Vogels의 기조연설 “Simplexity”에서 발표된 여섯 가지 핵심 원칙입니다. 이 개념은 “단순함(Simplicity)”과 “복잡성(Complexity)”의 조화를 통해, 복잡한 시스템과 조직을 효과적으로 관리하자는 메시지를 담고 있습니다.</p>
<h3 id="1-evolvability를-요구사항으로-두기">1. Evolvability를 요구사항으로 두기</h3>
<ul>
<li>시스템은 시간이 지남에 따라 성장하고 변화합니다.</li>
<li>S3를 설계할 때 “1년 후 아키텍처가 달라질 것”을 염두에 두고 구축했으며,</li>
<li>유지보수 중심의 단기 관점이 아니라 장기적 관점에서 변화에 대응 가능한 설계가 중요합니다.</li>
</ul>
<h3 id="2-복잡성을-조각내기-break-complexity-into-pieces">2. 복잡성을 조각내기 (Break complexity into pieces)</h3>
<ul>
<li>예를 들어 CloudWatch는 반복적으로 내부 구조를 분해·재구성하며 확장성을 유지했듯,</li>
<li>낮은 결합도와 높은 응집력, 그리고 잘 정의된 API를 가진 모듈형 구조가 핵심입니다.</li>
</ul>
<h3 id="3-조직-구조는-아키텍처를-따라-정렬하라">3. 조직 구조는 아키텍처를 따라 정렬하라</h3>
<ul>
<li>소프트웨어 아키텍처의 복잡성이 곧 조직 구조의 복잡성으로 이어집니다.</li>
<li>팀 단위의 소유권(ownership)과 구조적 도전 정신이 필요하며,</li>
<li>조직 운영 역시 시스템처럼 수시로 점검하고 개선해야 합니다.</li>
</ul>
<h3 id="4-cell-기반-구조로-나누기">4. Cell 기반 구조로 나누기</h3>
<ul>
<li>서비스나 조직을 작업 단위(cell)로 분리하면,</li>
<li>인접한 영역의 문제 확산을 방지할 수 있으며,</li>
<li>각 cell이 독립적으로 확장 및 테스트될 수 있어 신뢰성과 보안을 동시에 보장합니다.</li>
</ul>
<h3 id="5-예측-가능한-시스템-설계">5. 예측 가능한 시스템 설계</h3>
<ul>
<li>이벤트 중심(event-driven) 아키텍처처럼, 반응 흐름을 명확히 정의하면</li>
<li>시스템 동작이 예측 가능해지고, 불확실성에 대한 민감도를 낮출 수 있습니다 (예: Route 53)</li>
</ul>
<h3 id="6-복잡성은-자동화하라">6. 복잡성은 자동화하라</h3>
<ul>
<li>“무엇을 자동화할 것인가?”가 아니라,</li>
<li>“무엇을 자동화하지 않을 것인가?”를 묻는 것이 핵심.</li>
<li>판단이 덜 필요한 반복적 작업은 가급적 자동화하여,</li>
<li>사람은 판단이 필요한 영역에 집중할 수 있게 합니다 (예: Bedrock Serverless Agentic Workflows)</li>
</ul>
<hr>
<p>💭 이번이 첫 AWS 컨퍼런스 참석이었기 때문에, 전 세계적으로 기술적 영향력을 갖춘 아마존 CTO의 철학을 직접 들을 수 있다는 점만으로도 큰 감명을 받았습니다. 특히 우리가 일상적으로 사용하고 있는 AWS 서비스를 예시로 들며 설명해주셔서 내용이 더욱 와닿았고, 신선하게 느껴졌습니다.</p>
<p>여섯 가지 원칙 모두 중요했지만, 특히 2번 ‘복잡성 분해’와 4번 ‘셀 기반 구조’는 동료 네오께서도 평소 피드백을 주셨던 부분이라 더욱 깊이 새길 수 있었고, 6번 ‘자동화’ 원칙은 개인적으로 작년부터 관심을 가져온 주제라 특히 인상 깊었습니다. 지금까지는 “무엇을 자동화할 것인가”에만 집중했다면, “무엇을 자동화하지 않을 것인가”라는 관점은 저에게 매우 신선한 인사이트로 다가왔습니다.</p>
<h1 id="나의-완벽한-개발-비서-amazon-q와-함께-sdlc-이븐하게-가속하기">나의 완벽한 개발 비서: Amazon Q와 함께 SDLC 이븐하게 가속하기</h1>
<p>이번 AWS Summit Seoul 2025에서 특히 관심 있었던 세션 중 하나는 “나의 완벽한 개발 비서: Amazon Q와 함께 SDLC 이븐하게 가속하기”였습니다.
개발자의 입장에서 하루에도 수없이 반복되는 작업들—문서 탐색, 테스트 코드 작성, 디버깅 등—이 Amazon Q를 통해 어떻게 자동화되고 최적화되는지를 직접 확인할 수 있던 유익한 시간이었습니다.</p>
<p>Amazon Q는 단순한 코드 생성 도구가 아니라, SDLC(소프트웨어 개발 생명주기)의 전 구간을 지원하는 지능형 파트너라는 점이 특히 인상 깊었습니다. 예를 들어, 기획 단계에서는 방대한 문서나 운영 가이드를 빠르게 탐색할 수 있었고, 실제 개발 환경(IDE) 안에서는 코드 보완, 테스트 자동 생성, 심지어는 보안 스캔까지 지원했습니다.</p>
<p>세션 중 가장 흥미로웠던 부분은 기존 Java 애플리케이션을 현대화하는 데모였습니다. 대화형으로 코드를 분석하고, 테스트 코드를 생성하며, 마이그레이션 방향까지 제시하는 모습에서 “정말 이게 현실이구나” 하는 감탄이 나왔습니다.
특히 “Amazon Q Agent”를 활용한 대화 기반 리팩토링 과정은 마치 경험 많은 동료 개발자와 페어 프로그래밍하는 느낌이 들 정도였습니다.</p>
<p>무엇보다도, 사내 데이터를 학습한 전용 비서처럼 동작하면서도 코드 유출 없이 보안까지 신경 쓴 설계는 기업 입장에서 매우 매력적인 요소였습니다. 단순한 AI 도우미를 넘어, 현실적인 업무 흐름에 녹아드는 도구라는 점에서 실무 적용 가능성도 높아 보였습니다.</p>
<p>이번 세션을 통해 ‘자동화’에 대한 관점을 다시 한번 생각해보게 되었고, 반복적인 작업에서 벗어나 더 창의적이고 전략적인 업무에 집중할 수 있는 가능성을 체감할 수 있었습니다. 개발자로서의 일상이 어떻게 변화할 수 있을지를 미리 엿본 느낌이었습니다.</p>
<hr>
<p>💭 우리 팀은 이미 PRD/UXDR 같은 요구사항 정의나 ADR 기반의 설계를 체계적으로 하고 있기 때문에, Amazon Q가 이러한 초기 기획 및 설계 단계에서 어떻게 도움을 줄 수 있을지가 가장 궁금했던 부분이었습니다. 아쉽게도 이번에는 체험 부스를 직접 방문해 보지는 못해 그 부분은 확인하지 못했지만,
한편으로는 데브옵스 팀에서 Amazon Q를 활용한 코드 리뷰 환경을 마련해주신 덕분에, 현재는 두 개의 레포에서 실험적으로 적용해보고 있는 중입니다.</p>
<h1 id="✨-숨고-1000만-가입자를-이끈-플랫폼의-클라우드-활용-전략-및-혁신-스토리">✨ 숨고 1,000만 가입자를 이끈 플랫폼의 클라우드 활용 전략 및 혁신 스토리</h1>
<hr>
<p>💭 이번 세션을 통해 숨고의 시작부터 현재까지의 성장 과정과 기술적 진화를 되짚어볼 수 있어 의미 있는 시간이었습니다. 제가 입사하기 전부터 이어져 온 숨고의 변화와 확장 과정을 한눈에 정리해볼 수 있었고, 기술이 비즈니스 성장에 어떻게 기여했는지를 전체 맥락에서 이해할 수 있었습니다.</p>
<p>특히 데브옵스 영역은 평소 직접 접할 기회가 적었던 만큼, 많은 동료들의 노력과 기술적 도약이 어떤 식으로 플랫폼에 녹아들었는지를 새삼 실감할 수 있었습니다. 반면, 제가 입사한 이후의 여정에서는 직접 알고 있거나 일부 기여한 부분들도 있어, 개인적으로 더욱 몰입해서 들을 수 있었습니다.</p>
<p>또한 MAS 이전에는 배포(빌드)에 20분 이상 소요되던 시기가 있었다는 점이 인상 깊었고, 실제로 당시 상황이 꽤나 고되었을 거라는 생각도 들었습니다. 현재는 자주 있는 상황은 아니지만, MAS 서버에 포함된 알림 관련 작업이 배포될 경우 서버 수 증가로 인해 여전히 20분 이상 소요되는 경우도 발생하고 있어, 이 부분은 여전히 고민이 필요한 과제라는 생각도 들었습니다.</p>
<p>이번 세션은 단순한 회고를 넘어, 기술적 의사결정의 맥락을 공유하고, 앞으로의 방향을 고민할 수 있는 기회였다는 점에서 매우 유익했습니다.</p>
<h1 id="변화하는-rdb-데이터를-놓치지-않는-실시간-스트리밍-아키텍처-이야기">변화하는 RDB 데이터를 놓치지 않는 실시간 스트리밍 아키텍처 이야기</h1>
<p>이번 세션에서는 RDB의 데이터 변경 사항을 실시간으로 감지하고, 이를 안정적으로 스트리밍하는 아키텍처를 어떻게 구현했는지에 대한 구체적인 사례를 들을 수 있었습니다.</p>
<p>핵심은 CDC(Change Data Capture) 기술을 기반으로, 변경된 데이터를 Kafka로 전달하고, 이후 이를 다양한 다운스트림 시스템(예: 데이터 레이크, 모니터링, 실시간 알림 등)으로 유실 없이 안정적으로 전달하는 구조였습니다.
Debezium, MSK, Kafka Connect 등 오픈소스와 AWS 서비스를 조합하여 신뢰성과 확장성을 모두 고려한 점이 인상 깊었습니다.</p>
<p>특히 다음과 같은 포인트들이 유익했습니다:</p>
<ul>
<li>CDC를 통해 RDB 기반 시스템에서도 실시간 이벤트 처리 구조로 전환 가능</li>
<li>중복 처리, 순서 보장, 재처리 가능성 등 스트리밍 환경의 현실적 고려사항을 어떻게 풀었는지에 대한 설명</li>
<li>실시간성과 정확성 사이의 트레이드오프를 실용적인 기준으로 접근한 사례</li>
</ul>
<p>개인적으로는 “어떤 데이터를 어떤 시점에 어떤 방식으로 처리할 것인가”에 대한 사고 전환의 계기가 되었고, 앞으로 데이터 흐름을 더 유연하고 실시간 중심으로 설계하는 데에 참고할 수 있는 좋은 인사이트를 얻었습니다.</p>
<hr>
<p>💭 무신사의 세션을 통해 Kafka Streams라는 기술을 새롭게 알게 되어 매우 유익했고, 공통 데이터(예: 사용자 정보 등)를 각 도메인별로 중복 테이블로 관리한다는 접근도 인상적이었습니다. 또한, 데이터옵스 챕터의 윌크께서 Kafka Streams를 이미 알고 계시고 실험적으로 적용하고 계신다는 점도 큰 참고가 되었습니다.
우리는 주로 주소나 프로필 정보 변경을 감지해 실시간 동기화하는 데 활용하고 있지만, 리콘랩스에서는 변경 데이터를 기반으로 트래커 생성이나 알림 트리거 등 다양한 방식으로 확장하고 있다는 점도 흥미롭고, 앞으로의 활용 방향에 대한 좋은 인사이트가 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MongoDB .local Seoul 2024]]></title>
            <link>https://velog.io/@ggg9_/MongoDB-.local-Seoul-2024</link>
            <guid>https://velog.io/@ggg9_/MongoDB-.local-Seoul-2024</guid>
            <pubDate>Wed, 13 Aug 2025 14:36:28 GMT</pubDate>
            <description><![CDATA[<h1 id="사용사례-삼성전자">사용사례: 삼성전자</h1>
<hr>
<h2 id="big-data-assistant">Big Data Assistant</h2>
<p><img src="https://velog.velcdn.com/images/ggg9_/post/b4ba81c0-545a-4aa5-811a-06be51c020a7/image.jpeg" alt=""><img src="https://velog.velcdn.com/images/ggg9_/post/c93975f4-64b1-4b35-bad3-3ff9066274cb/image.jpeg" alt=""><img src="https://velog.velcdn.com/images/ggg9_/post/73703e53-766f-465b-b9b3-650d2916af0e/image.jpeg" alt=""></p>
<p>첫번째 사진의 비전을 가지고, 세번째 사진의 프로세스를 수행하는 AI 기반의 데이터 질의 서비스 구축 여정을 발표해주셨다.</p>
<p>계기는 데이터팀에 들어오는 요청 분석 결과 데이터 추출 요청 및 단순 질의를 포함한 단순 작업이 65%를 차지하고, 20일 이상 소요되는 일감(2번째 사진 참고)들도 요구사항 ↔︎ 결과 커뮤니케이션 미스로 여러 번 재작업하면서 소모되는 리소스 문제를 해결하고자하는 이유도 포함되어 있었다.</p>
<p>백엔드 입장에서 구축 여정의 기술적인 부분은 모르는 부분이 많았지만, 올해 운영 대응과 그에 따른 단순 스크립트 작업을 많이하여 난이도 별 분석 요청 그래프가 크게 공감되었고, 미리 손 쓰지 못해 반복되는 단순 작업에 대한 리소스 낭비가 아쉬웠던터라 문제를 해결하려는 해당 프로젝트가 감명 깊었다.</p>
<h1 id="mongodb-80">MongoDB 8.0</h1>
<hr>
<blockquote>
<ul>
<li>resharding 50배 이상 빨라짐</li>
</ul>
</blockquote>
<ul>
<li>shard key를 변경하지 않아도 됨</li>
<li>추가 비용 없이 샤딩 가능</li>
</ul>
<p>동시 통역을 해주셨지만 예,,? 잘 못 들었슴돠,,?</p>
<p>🔗 <a href="https://www.mongodb.com/docs/upcoming/release-notes/8.0/">공식 문서</a>를 첨부합니다..</p>
<h1 id="모델링-원리와-방법론">모델링 원리와 방법론</h1>
<hr>
<p>모델링에 정답은 없다지만, 그래서 어려운 모델링이라 들어본 세션.</p>
<p>도큐먼트 필드 유형에서 배열과 Nested의 성능 차이가 궁금했었는데 큰 차이는 없다는 것을 알게되어 좋았고,</p>
<p>설계 전에 워크로드를 파악하여 읽기/쓰기의 빈도는 물론이고 데이터의 수명과 내구성도 고려할 수 있도록 공부를 더 해야겠다고 생각했다.</p>
<p>우리는 빅쿼리를 사용하고 있어 고려 대상은 아니겠지만, 빅쿼리를 사용하지 않는다면 분석용 DB로 Hidden Secondary를 활용할 수 있겠다는 생각도 해볼 수 있었다.</p>
<p>NoSQL인 mongoDB에서 Embedding vs Reference는 항상 고민되는 주제일 것인데 비교하여 고민 포인트와 가이드를 제시해주어 추후 설계 시 참고하면 좋을 것 같은 내용이 있었고, 기본적인 디자인 패턴을 크게 분류하여 예시해주었다.</p>
<p>예시된 패턴에서 Computed Pattern은 애플리케이션이 아닌 데이터베이스에서 연산하는 것이 그래도 되나,,? 미심쩍긴하지만 마켓에서도 활용 가능해보였고, Schema Versioning Pattern은 chat-service에서 폴이 사용한 것을 보고 따라하고는 있지만, 사용 및 관리 방식이 담당자에 따라 다른듯하여 내부적으로 컨벤션이 맞춰지면 좋을 것 같다고 생각했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] 책임 연쇄 패턴]]></title>
            <link>https://velog.io/@ggg9_/Design-Pattern-%EC%B1%85%EC%9E%84-%EC%97%B0%EC%87%84-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ggg9_/Design-Pattern-%EC%B1%85%EC%9E%84-%EC%97%B0%EC%87%84-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 30 May 2022 18:20:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Chain of Responsibility란? 
어떠한 요청이 들어왔을 때 요청을 받은 객체가 해당 요청을 처리할 수 없을 경우 연결된 다음 객체에 전달하고 해당 요청을 해결할 수 있는 객체가 처리하는 방식으로 요청을 처리할 수 있는 객체가 명시적이지 않을 때 사용할 수 있는 패턴이다.</p>
</blockquote>
<h3 id="장점">장점</h3>
<ul>
<li>작업을 수행하는 객체에서 작업을 호출하는 객체를 쉽게 분리할 수 있다.</li>
<li>사슬에 들어가는 객체를 바꾸거나 순서를 바꿈으로써 상황에 따라 동적으로 핸들러를 추가하거나 제거할 수 있으며 전체 구조를 손상시키지 않고 새로운 객체를 도입할 수 있다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>요청이 반드시 수행된다는 보장이 없다. 체인이 적절하게 구성되어 있지 않다면 사슬의 끝을 만날때까지 처리되지 않을 수도 있다.</li>
<li>요청을 처리하는데 걸리는 시간을 정확히 예측하기가 힘들다. </li>
<li>과정을 탐색하거나 디버깅하기가 힘들 수 있다.</li>
</ul>
<p>|참고
<a href="https://www.nextree.co.kr/p2533/">https://www.nextree.co.kr/p2533/</a>
<a href="https://blog.naver.com/pmw9440/222750365968">https://blog.naver.com/pmw9440/222750365968</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Design Pattern] 프록시 패턴]]></title>
            <link>https://velog.io/@ggg9_/Design-Pattern-%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@ggg9_/Design-Pattern-%ED%94%84%EB%A1%9D%EC%8B%9C-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 30 May 2022 10:45:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프록시 패턴(Proxy Pattern)이란?
프록시는 무언가를 대신 처리하는 의미로 어떠한 객체를 사용하고자 할때 객체를 직접적으로 참조하는것이 아니라 해당 객체를 대리(proxy)하는 객체를 통해 대상 객체에 접근하는 방식으로 객체에 대한 액세스를 제어한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ggg9_/post/e7988ce5-235a-4966-ad5e-3696b20fc25c/image.png" alt="">
<img src="https://velog.velcdn.com/images/ggg9_/post/21d67e52-b841-4bfa-b9cb-cce6b6c3dfd0/image.png" alt=""></p>
<p>프록시를 이용하면 캐싱이나 유효성 검사와 같은 처리를 통해 부하를 줄일 수 있고 실제 객체의 기능이 필요한 시점까지 객체의 생성을 미룰 수 있다는 장점과 동시에 클라이언트에서 프록시를 거쳐 객체에 도달하므로 응답이 늦어질 수 있다는 단점이 있다.</p>
<h3 id="프록시의-종류">프록시의 종류</h3>
<h4 id="가상-프록시virtual-proxy">가상 프록시(Virtual Proxy)</h4>
<ul>
<li>많은 리소스가 요구될때 필요한 시점까지 객체의 생성을 지연하면서 해당 객체가 생성된 것 처럼 동작하고 싶을때 사용하는 패턴.
<code>ex</code> 해상도가 높은 이미지와 텍스트가 함께 있는 페이지를 표시 할 경우 먼저 로딩된 텍스트를 먼저 표시되도록 작업 분산</li>
</ul>
<h4 id="원격-프록시remote-proxy">원격 프록시(Remote Proxy)</h4>
<ul>
<li>서로 다른 주소 공간에 있는 객체에 대해 같은 주소 공간에 있는 것처럼 동작하게 만드는 패턴.
<code>ex</code> Google Docs</li>
</ul>
<h4 id="보호-프록시protection-proxy">보호 프록시(Protection Proxy)</h4>
<ul>
<li>대상 객체에 대한 접근을 제어하거나 객체마다 접근 권한을 다르게 하고 싶은 경우에 사용하는 패턴.</li>
</ul>
<p>|참고
<a href="https://refactoring.guru/design-patterns/proxy">https://refactoring.guru/design-patterns/proxy</a>
<a href="https://m.blog.naver.com/anciid/221796576972">https://m.blog.naver.com/anciid/221796576972</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flask] Blueprint를 이용한 모듈화]]></title>
            <link>https://velog.io/@ggg9_/Flask-Blueprint%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%AA%A8%EB%93%88%ED%99%94</link>
            <guid>https://velog.io/@ggg9_/Flask-Blueprint%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%AA%A8%EB%93%88%ED%99%94</guid>
            <pubDate>Sun, 10 Oct 2021 09:37:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Blueprint란 ?
플라스크는 어플리케이션 컴포넌트를 만들고 어플리케이션 내부나 어플리케이션간의 공통 패턴을 지원하기 위해 블루프린트(blueprint)라는 개념을 사용한다. 
블루프린트는 어플리케이션이 동작하는 방식을 단순화하고 중앙 집중된 등록 수단을 제공한다.</p>
</blockquote>
<ul>
<li>Flask는 라우트 함수를 이용하여 url을 생성하고 데코레이터 형식으로 매핑하게 되는데 기능이 추가될수록 라우트 함수도 늘어나게 되고 app에 계속 추가해 주어야 한다.
이때 블루프린트를 이용하면 Django의 프로젝트 urls.py와 같이 깔끔하게 관리할 수 있다.</li>
</ul>
<h3 id="blueprint-객체-생성">Blueprint 객체 생성</h3>
<pre><code class="language-python"># user.py
from flask import Blueprint 

bp = Blueprint(&#39;user&#39;, __name__)

@bp.route(&#39;/signup&#39;, methods=[&#39;POST&#39;])
def signup():
    return jsonify()</code></pre>
<p>user라는 이름의 블루프린트 객체를 bp로 생성하고 route 함수를 app이 아닌 bp에서 호출한다.</p>
<h3 id="blueprint-등록">Blueprint 등록</h3>
<pre><code class="language-python">from flask import Flask
from . import user

app = Flask(__name__)

app.register_blueprint(user.bp)
# app.register_blueprint(user.bp, url_prefix=&#39;/admin&#39;)</code></pre>
<p><code>app.py</code> 또는 <code>__init__.py</code>와 같이 Flask 객체가 생성된 곳에서 등록할 블루프린트 객체가 있는 파일을 import 해주고 app에 블루프린트 객체를 등록해 준다.</p>
<p>필요시 <code>url_prefix</code>를 이용하여 접두어를 지정할 수 있다. 
위의 주석처럼 prefix를 지정하고 user.py의 /singup을 요청한다면 <code>http://localhost:5000/admin/singup</code> 과 같은 url로 호출된다.</p>
<br>

<p><strong>|</strong> 참고
<a href="https://flask-docs-kr.readthedocs.io/ko/latest/blueprints.html">Flask-docs</a>
<a href="https://wikidocs.net/81510">점프 투 플라스크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flask] MethodView]]></title>
            <link>https://velog.io/@ggg9_/Flask-MethodView</link>
            <guid>https://velog.io/@ggg9_/Flask-MethodView</guid>
            <pubDate>Mon, 04 Oct 2021 09:34:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Django View와 같은 클래스 기반 Pluggable View MethodView</p>
</blockquote>
<p>HTTP 메서드와 동일한 이름의 클래스 메소드에 매핑된다.</p>
<pre><code class="language-python">from flask.views import MethodView

class TestView(MethodView):

    def post(self):
        pass

    def get(self):
        pass

app.add_url_rule(&#39;/test&#39;, view_func=TestView.as_view(&#39;test&#39;))</code></pre>
<h3 id="url-route">URL Route</h3>
<pre><code class="language-python">class TestView(MethodView):

    def post(self):
        pass

    def get(self, test_id):
        if test_id is None:
            pass
        else:
            pass

    def patch(self):
        pass

    def delete(self, test_id):
        pass

test_view = TestView.as_view(&#39;test&#39;)
app.add_url_rule(&#39;/test&#39;, view_func=test_view, methods=[&#39;POST&#39;, &#39;PATCH&#39;])
app.add_url_rule(&#39;/test&#39;, defaults={&#39;test_id&#39;:None}, view_func=test_view, mehtods=[&#39;GET&#39;])
app.add_url_rule(&#39;/test/&lt;int:test_id&gt;&#39;, view_func=test_view, methods=[&#39;GET&#39;, &#39;DELETE&#39;])</code></pre>
<p>path parameter가 있거나 여러 메소드가 있는 경우 위와 같이 규칙을 정의한다.</p>
<h3 id="데코레이터">데코레이터</h3>
<pre><code class="language-python">class TestView(MethodView):
    decorators = [decorator]</code></pre>
<p>클래스 선언 후 데코레이터 리스트를 지정해주면 하위에 정의된 모든 메소드에 데코레이터가 적용된다.
개별 메소드에서 데코레이터가 필요한 경우 메소드 위에 데코레이터를 달아준다.</p>
<pre><code class="language-python">class TestView(MethodView):

    @decorator
    def get(self):
        pass</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] 다른 테이블 컬럼 값으로 업데이트하기 (feat.GROUP BY)]]></title>
            <link>https://velog.io/@ggg9_/MariaDB-%EB%8B%A4%EB%A5%B8-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%BB%AC%EB%9F%BC-%EA%B0%92%EC%9C%BC%EB%A1%9C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%ED%95%98%EA%B8%B0-feat.GROUP-BY</link>
            <guid>https://velog.io/@ggg9_/MariaDB-%EB%8B%A4%EB%A5%B8-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%BB%AC%EB%9F%BC-%EA%B0%92%EC%9C%BC%EB%A1%9C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%ED%95%98%EA%B8%B0-feat.GROUP-BY</guid>
            <pubDate>Mon, 27 Sep 2021 09:22:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ggg9_/post/b08cc7af-c096-4c36-8c8b-cdc9f5c0c843/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.45.42.png" alt=""></p>
<blockquote>
<p>products 테이블의 min_price 컬럼을 연결된 items 테이블의 price 컬럼들 중 최솟값으로 업데이트</p>
</blockquote>
<pre><code class="language-sql"># items
+---------+-------+------------+
| item_id | price | product_id |
+---------+-------+------------+
|       1 | 30000 |          1 |
|       2 | 30000 |          1 |
|       3 | 10000 |          1 |
+---------+-------+------------+

# products
+------------+-----------+
| product_id | min_price |
+------------+-----------+
|          1 |     20000 |
+------------+-----------+</code></pre>
<p>위와 같은 데이터를 가졌다고 가정하였을때 <code>items</code> 테이블의 <code>product_id</code>가 <code>1</code>인 아이템의 가격들 중 최솟값과 <code>products</code> 테이블의 <code>product_id</code>가 1인 상품의 최소가격이 일치하지 않는 경우를 먼저 구해보면 아래와 같은 결과를 얻을 수 있다.</p>
<pre><code class="language-sql"># query
SELECT p.product_id , p.min_price , min(i.price) item_min
FROM products p
JOIN items i ON
p.product_id = i.product_id
GROUP BY p.product_id
HAVING p.min_price != item_min

# result
+------------+-----------+----------+
| product_id | min_price | item_min |
+------------+-----------+----------+
|          1 |     20000 |   10000  |
+--------==--+-----------+----------+</code></pre>
<p>얻은 결과에서 <code>products</code> 테이블의 <code>min_price</code> 값을 <code>item_min</code>의 값으로 업데이트하여 일치시켜 보자.</p>
<pre><code class="language-sql"># query
UPDATE products p
INNER JOIN (SELECT p.product_id , p.min_price , min(i.price) item_min
FROM products p
JOIN items i ON
p.product_id = i.product_id
GROUP BY p.product_id) t ON
p.id = t.id SET
p.min_price = t.item_min
WHERE t.min_price != t.item_min

# 짜란✨
+------------+-----------+----------+
| product_id | min_price | item_min |
+------------+-----------+----------+
|          1 |     10000 |   10000  |
+--------==--+-----------+----------+</code></pre>
<p>👉 UPDATE 절에서의 GROUP BY가 필요한 경우 GROUP BY 결과에 INNER JOIN 하여 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MariaDB] 내 주변 반경 n km 이내 데이터 가져오기]]></title>
            <link>https://velog.io/@ggg9_/MariaDB-%EB%82%B4-%EC%A3%BC%EB%B3%80-%EB%B0%98%EA%B2%BD-n-km-%EC%9D%B4%EB%82%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</link>
            <guid>https://velog.io/@ggg9_/MariaDB-%EB%82%B4-%EC%A3%BC%EB%B3%80-%EB%B0%98%EA%B2%BD-n-km-%EC%9D%B4%EB%82%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0</guid>
            <pubDate>Mon, 27 Sep 2021 07:29:08 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-sql">SELECT
    (
      6371 * acos (
      cos ( radians( {목적지 위도} ) )
      * cos( radians( {내위치 위도} ) )
      * cos( radians( {목적지 경도} ) - radians( {내위치 경도} ) )
      + sin ( radians( {목적지 위도} ) )
      * sin( radians( {내위치 위도} ) )
    )
) AS distance
FROM places
HAVING distance &lt; {원하는 반경 ex.500m면 0.5}
ORDER BY distance;</code></pre>
<p>6371은 지구 반지름을 km로 나타낸 것으로 그냥 두고 radians의 괄호안의 값을 바꾸어 계산한다.
원하는 반경은 HAVING 절을 이용하여 조절 할 수 있다.
나는 places 테이블에 latitude와 longitude 컬럼이 있어 컬럼 값을 가지고와 계산하였고, 
그 중 10km 이내 데이터를 조회하였다.</p>
<pre><code class="language-sql">SELECT
    (
      6371 * acos (
      cos ( radians(latitude) )
      * cos( radians(37.56726109866003) )
      * cos( radians(longitude) - radians(127.1903672271528) )
      + sin ( radians(latitude) )
      * sin( radians(37.56726109866003) )
    )
) AS distance
FROM places
HAVING distance &lt; 10
ORDER BY distance
LIMIT 0 , 10;</code></pre>
<pre><code>✨ result
+-------------------+
| distance          |
+-------------------+
| 5.183107413771042 |
| 5.325682485437883 |
|  5.76121672748217 |
| 7.224819777240822 |
| 8.514181604475779 |
| 8.950925292675505 |
| 9.241103258775375 |
| 9.249974510246574 |
| 9.314941889143482 |
| 9.464445622192283 |
+-------------------+</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Mariadb] WITH ROLLUP을 이용한 합계 나타내기 (feat.NULL)]]></title>
            <link>https://velog.io/@ggg9_/Mariadb-WITH-ROLLUP%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%95%A9%EA%B3%84-%EB%82%98%ED%83%80%EB%82%B4%EA%B8%B0-feat.NULL</link>
            <guid>https://velog.io/@ggg9_/Mariadb-WITH-ROLLUP%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%95%A9%EA%B3%84-%EB%82%98%ED%83%80%EB%82%B4%EA%B8%B0-feat.NULL</guid>
            <pubDate>Fri, 17 Sep 2021 01:56:50 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-sql">SELECT food, COUNT(IFNULL(food, 1)) cnt FROM likes GROUP BY food

# result
+------+-------------+
| food |         cnt |
+--------------------+
| NULL |           2 |
|  냉삼 |          10 |
|  곱창 |           5 |
+------+-------------+</code></pre>
<blockquote>
<p>WITH ROLLUP을 이용하여 GROUP BY 결과에 대한 합계 행을 추가해보자.</p>
</blockquote>
<h3 id="일단-rollup">일단 ROLLUP</h3>
<pre><code class="language-sql">SELECT food, COUNT(IFNULL(food, 1)) cnt FROM likes GROUP BY food WITH ROLLUP

+------+-------------+
| food |         cnt |
+--------------------+
| NULL |           2 |
|  냉삼 |          10 |
|  곱창 |           5 |
| NULL |          17 |  👈
+------+-------------+</code></pre>
<p>위와 같이 WITH ROLLUP을 이용하면 합계 행이 추가되었으나 GROUP BY <strong>기준이되는 열의 값은 NULL로 표시된다.</strong> </p>
<blockquote>
<p>COALESCE를 이용하여 추가된 행이 합계라는것을 알려주자.</p>
</blockquote>
<h3 id="보기좋게-rollup">보기좋게 ROLLUP</h3>
<pre><code class="language-sql">SELECT COALESCE(food, &#39;합계&#39;) food, COUNT(IFNULL(food, 1)) cnt
FROM likes
GROUP BY food WITH ROLLUP


+------+-------------+
| food |         cnt |
+--------------------+
|  합계 |           2 |  👈
|  냉삼 |          10 |
|  곱창 |           5 |
|  합계 |          17 |
+------+-------------+</code></pre>
<p>????????</p>
<p>COALESCE는 <strong>NULL값을 지정된 기본값으로 대체하는 함수</strong>로 진짜 NULL까지 지정된 &#39;합계&#39;로 대체되어 버렸다. </p>
<blockquote>
<p>진짜 NULL을 되돌려주자.</p>
</blockquote>
<h3 id="정확하게-rollup">정확하게 ROLLUP</h3>
<pre><code class="language-sql">SELECT
    COALESCE(t.coalesce_food, &#39;Total&#39;) AS food,           👈 [3]
    COUNT(IFNULL(t.food, 1)) AS cnt
FROM
(
    SELECT COALESCE(food, &#39;NULL&#39;) AS coalesce_food, food  👈 [1]
    FROM likes
) t
GROUP BY t.coalesce_food WITH ROLLUP                      👈 [2]

# 짜란✨
+------+-------------+
| food |         cnt |
+--------------------+
|  냉삼 |          10 |
|  곱창 |           5 |
| NULL |           2 |
|  합계 |          17 |
+------+-------------+

[1] 진짜 NULL을 COALESCE로 처리해준 결과에 
[2] WITH ROLLUP을 이용하여 합계 행을 추가하고 
[3] ROLLUP 행에 대해 COALESCE로 최종 &#39;합계&#39; 처리를 해주었는데 </code></pre>
<p><em>복잡하고 더 좋은 방법이 있을것만 같은 느낌..</em></p>
<h3 id="ifnull과-coalesce의-차이점이-궁금하다면-👇">IFNULL과 COALESCE의 차이점이 궁금하다면 👇</h3>
<p> <a href="https://stackoverflow.com/questions/18528468/what-is-the-difference-bewteen-ifnull-and-coalesce-in-mysql/18528590">COALESCE vs IFNULL</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Backup] Shell script를 이용하여 자동 백업하기 (feat.crontab)]]></title>
            <link>https://velog.io/@ggg9_/Backup-Shell-script%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9E%90%EB%8F%99-%EB%B0%B1%EC%97%85%ED%95%98%EA%B8%B0-feat.crontab-e8ml6uy7</link>
            <guid>https://velog.io/@ggg9_/Backup-Shell-script%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EC%9E%90%EB%8F%99-%EB%B0%B1%EC%97%85%ED%95%98%EA%B8%B0-feat.crontab-e8ml6uy7</guid>
            <pubDate>Fri, 30 Jul 2021 09:24:20 GMT</pubDate>
            <description><![CDATA[<p>🚨 n개월전 서버내 이미지가 몽.땅 날아가는 경험을 하고 <strong>백업의 중요성</strong>을 알게 되어 울면서 작성한 쉘 스크립트..</p>
<pre><code class="language-shell">#!/bin/bash

output=`df -hP |grep /dev/xvda1 |awk &#39;{print $5}&#39;`                                  [1]
use=$(echo $output | cut -d&#39;%&#39; -f1)                                                 [2]
if [ $use -ge 80 ]; then                                                            [3]
    message=&quot;*[WARN] 디스크 사용량 경고 &quot;$&quot;$output*&quot;$&#39;\n&#39;
    echo &quot;$message&quot;
    data=&quot;{\&quot;text\&quot;: \&quot;$message\&quot;}&quot;
    slack_webhook=&quot;생성된 Webhook URL&quot;
    curl -X POST -H &#39;Content-type: application/json&#39; --data &quot;$data&quot; $slack_webhook  [4]

else
    DATE=$(date +%Y%m%d)                                                            [5]
    DB_BACKUP_DIR=/backup/db
    APP_BACKUP_DIR=/backup/app

    mysqldump -u root -p&#39;password&#39; database명 &gt; $DB_BACKUP_DIR/$DATE.sql
    tar -zcf $APP_BACKUP_DIR/$DATE.tar.gz ./target
fi

[1] 디스크 사용량 확인 명령어 변수에 저장(위에서는 &#39;/dev/xvda1&#39; 디스크에 대한 용량 확인)
    df 명령어에 -hP 옵션을 주어 보기좋게(h) 한줄로(P) 5번째 필드인 &#39;Use%&#39; 값 출력
[2] [1] 명령어 실행 후 출력된 사용량 값에 &#39;%&#39; 문자열 제거하여 변수에 저장
[3] [2] 에서 저장된 값이 80 이상이면
[4] 슬랙 Webhook API 이용하여 메세지 발송
[5] 실행된 날짜의 백업 파일명 지정을 위해 date 함수를 이용한 변수 생성</code></pre>
<p>이제 크론탭을 이용하여 원하는 주기에 스크립트 실행 예약을 걸어주면 완성 !</p>
<p><a href="https://crontab.guru/">✨크론탭 시간 설정 시 굉장히 도움되는 사이트</a>
<a href="https://eunjin3786.tistory.com/150">Slack Webhook 참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Wecode] 인턴쉽 후기 (a.k.a 우당탕탕 ㄹㅂㄹㅂ)]]></title>
            <link>https://velog.io/@ggg9_/Wecode-%EA%B8%B0%EC%97%85%ED%98%91%EC%97%85-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@ggg9_/Wecode-%EA%B8%B0%EC%97%85%ED%98%91%EC%97%85-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 11 Apr 2021 06:51:46 GMT</pubDate>
            <description><![CDATA[<p>✨ 반려동물 돌보미 매칭 플랫폼 &#39;너의 스무살&#39; 개발 인턴쉽</p>
<h3 id="👨👩👧👧-front-end--back-end">👨‍👩‍👧‍👧 Front-end / Back-end</h3>
<p>FE | 박남준(PM), 김진희, 이연성
BE | 오가빈</p>
<p>2020.10.19 - 2020.11.12</p>
<h3 id="💻-tech-stack">💻 Tech stack</h3>
<p>AqueryTool
Django
Python
PyJWT / Bcrypt
MySQL
Gunicorn
Docker</p>
<h3 id="✨-구현-사항">✨ 구현 사항</h3>
<ul>
<li>백엔드 서버 초기세팅</li>
<li>데이터베이스 모델링</li>
<li>일반 회원가입 / 로그인</li>
<li>아이디 찾기</li>
<li>위치 검색어 자동완성</li>
<li>검색 필터(지역, 성별)</li>
<li>위치 검색어 자동완성 유닛테스트</li>
</ul>
<h3 id="🐾-review">🐾 Review</h3>
<p>~ 부제 : 엄청나게 뒷북치는 세상 뒤늦은 후기 ~</p>
<p>드디어 대망의 인턴쉽을 다녀오게 되었다.</p>
<p>백엔드 인턴으로는 혼자 참여하게 되었지만 위코드 선배 기수분이 백엔드 개발자로 근무중이라 하셔서 마음이 조금은 놓였다.</p>
<p>출근을 했더니 우리 자리에 이름과 환영 메세지가 인쇄되어 놓여져 있었다 😀</p>
<p>그렇게 시작된 한달간의 인턴쉽 후기 !</p>
<p>개발자로 실무에 투입되어 어쩔줄 몰라하는 우리에게 봉현님께서 현업 프로세스와 우리가 맡아 개발하게 될 플랫폼에 대해 자세히 설명을 해주셨다.</p>
<p>그런데 아직 기획이 나오지 않아 당장 개발을 시작 할 수 없는 상황이였고 그런 우리를 안타깝게 여기신 봉현님께서 기술 자료들과 취업과 관련된 꿀팁들을 마구마구 전수해주셔서 자칫하면 허비할 수 있는 시간을 알차게 보낼 수 있도록 도와주셨다.</p>
<p>나같은 경우에는 배포를 경험해보지 못했는데 그 부분을 공부해보면 좋겠다고 가이드를 잡아주셔서 배포를 해볼 수 있었고 그 이후에도 남는 시간에는 크롤링 과제를 내주셨다. 그런데 해보면 간단한 크롤링이 아닐거라고 하셨는데 정말 간단한 크롤링이 아니였다. (<em>동공지진👀</em>)</p>
<p>그러던중에 기획이 나오기 시작하여 개발에 들어갔는데 PM님께서 다시 전체적인 흐름에 대해 짚어주셨고 실무에서의 전체적인 프로세스를 정말 잘 설명해주셨다. 이외에도 오실때마다 세션 형식으로 많은 설명을 해주셨는데 정말 쉽게 알려주시는데도 이해가 안되는 부분은 말씀드리면 더 쉽게 다시 알려주셨다.</p>
<p>그리고 당시 깃도 익숙하지 않고 로컬에서만 작업해보았던 나는 ssh 서버를 이용하여 협업하는 방식에서 깃이 많이 헷갈렸었다.(<del>대환장파티 넘무서웠음</del>)
그래서 깃에 조금 더 숙련된 프론트분들께 도움을 많이 받았는데 그래도 헤매는 나를 보고 봉현님께서 ssh 서버와 깃 플로우에 대한 완벽 세션으로 구원 해주셨다. </p>
<p>그렇게 한달간의 인턴쉽을 진행하면서 기획의 중요성과 동시에 기획의 수정 가능성에 대해서도 염두해야 한다는 걸 많이 느꼈고 위코드 초반에 예리님께서 왜 백엔드는 큰그림을 볼 줄 알아야된다고 하셨는지 조금은 알 수 있을것만 같았다. (but, 한치 앞도 못보는 나..)</p>
<p>당시에는 더 많은 개발을 해보지 못한것이 아쉬웠는데 돌이켜보면 SSL이나 CI/CD라던지 설명 해주셨지만 직접 적용해보지 못한것들에 대한 아쉬움이 많이 남는다.</p>
<p>그리고 그때는 도커도 유닛테스트도 적용하고 마무리를 해야 내 스스로 자책이 덜 될 것 같아서 남준님 취업 축하파티(?)도 참여 못하고 (근데 안가길 잘했지..는 장난ㅋ) 백엔드와 프론트엔드의 작업 속도랄까 작업 순서의 차이가 다른게 조금 외롭고 속상한 티를 냈던 것 같아 죄송하다. 여러분.. (근데 슬랙에서 마침 도커와 유닛테스트에 치여 밤새고 계시던 수현님과의 짧은 대화 눈물날만큼 반가웠어요..🥺)</p>
<p>역시 혼자는 힘들다ㅎ (feat.강ㄱㅎ)
그치만 멋져..</p>
<h3 id="근데-무엇보다-너무너무너무x100000-재밌었다-우리팀-미친조합">근데 무엇보다 너무너무너무x100000 재밌었다. (우리팀 <del>미친</del>조합)</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Wecode] Arket 클론 프로젝트 후기]]></title>
            <link>https://velog.io/@ggg9_/Wecode-Arket-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@ggg9_/Wecode-Arket-%ED%81%B4%EB%A1%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sat, 03 Apr 2021 09:05:39 GMT</pubDate>
            <description><![CDATA[<p>✨ 에이치엔엠(H&amp;M)의 서브 고급화 브랜드 아르켓 온라인스토어 클론 프로젝트</p>
<h3 id="👨👩👧👧-front-end--back-end">👨‍👩‍👧‍👧 Front-end / Back-end</h3>
<p>FE | 김진희(PM), 손종일, 윤시훈, 정원태
BE | 이도길, 오가빈</p>
<p>2020.09.14 - 2020.09.25</p>
<h3 id="💻-tech-stack">💻 Tech stack</h3>
<p>AqueryTool
Django
Python
PyJWT / Bcrypt
MySQL</p>
<h3 id="database-erd">Database ERD</h3>
<p><img src="https://images.velog.io/images/ggg9_/post/14c77488-dd13-4589-904c-3f8ac3e917a5/image.png" alt=""></p>
<h3 id="✨-구현-사항">✨ 구현 사항</h3>
<ul>
<li>Project setting</li>
<li>Data Modeling</li>
<li>Signup / Signin</li>
<li>Cart</li>
<li>Unit test</li>
</ul>
<h3 id="👕-review">👕 Review</h3>
<p>~ 부제 : 엄청나게 뒷북치는 세상 뒤늦은 후기 ~</p>
<p>2차 프로젝트는 1차 프로젝트와는 다르게 크롤링을 하지 않고 저작권이 없는 이미지와 더미 데이터를 생성하여 진행하게 되었다. (<a href="https://velog.io/@ggg9_/%ED%9A%8C%EA%B3%A0%EB%A1%9D-Project-Naweki">나이키 사건</a>때문이야요 ?.. 👀)</p>
<p>우리가 클론하게 된 아르켓 사이트는 메인 페이지부터 감각적인 이미지로 채워져있고 상품 이미지도 깔끔하게 이루어진 쇼핑몰이였기때문에 이번에도 청천벽력 같은 소식이었지만 좋은 경험..☆</p>
<p>이번 프로젝트에서는 일반 회원가입과 로그인, 장바구니 기능을 맡게 되었는데 일반 회원가입, 로그인은 1차 프로젝트가 끝나고 같은 팀이였던 수현님과 따로 만나 서로 담당했던 기능을 바꿔서 진행해보았기 때문에 어렵지 않았다. (스현님 감삼당🙏🏻)</p>
<p>문제는 장바구니였다. 당시에는 어떤 로직으로 풀어내야 할지 어떤 메소드로 통신은 어떻게 해야하는지 예를들면 POST와 PATCH를 왜 구분하여야 하는지 구분하여 사용한다면 PATCH는 또 뭣이고 PUT은 또 뭣인지 혼돈의 도가니였다. 기능 자체가 이해 되지 않는 느낌이 많이들었다. 그래서 어느정도 이해가 될 때까지 고민도 많이하고 질문도 도움도 많이 받았다. (모두들 감삼당..🙏)</p>
<p>기능을 구현하면서 직접 사용해보니 RESTful API와 메소드에 대한 이해도가 많이 높아졌고 질문을 했지만 제대로 이해하지 못했던 답변들도 하나씩 이해되기 시작하였다.</p>
<p>또 이번 프로젝트에서는 유닛테스트와 깃 리베이스를 이용한 커밋 내역 관리도 해보게 되었는데 
유닛테스트를 처음 접하였을때는 내가 짠 테스트코드가 의심되었고 포스트맨이나 httpie 등으로 테스트해보면서 코드를 짤텐데 왜 중요한 작업일까 ? 생각하였다.</p>
<p>하지만 직접 테스트코드를 작성해보니 내가 짠 테스트코드도 작동을 잘 하고 포스트맨이나 httpie 등을 이용하여도 놓칠 수 있는 부분이 있으며 추후 관리에 있어서도 유닛테스트가 엄청 중요한 작업이구나.. 라는걸 느낄 수 있었던 알찬 프로젝트였다.</p>
<p>하지만 모든 팀원들과 통신을 해보지 못해 아쉬움도 많이 남는 프로젝트기도하다😥 (그만큼 도길님이 고생하셨겠쥬 ?.. :gom_ak:)
도길님과는 돌이켜보면 프로젝트 초반에는 계속 어디론가 떠나셔서 슬펐는데 가끔씩 돌아오셨을때 막혀있으면 봐주고 가시고(따로, 또 같이의 정석ㅎ👍) 그게 정말 잘 맞았던 것 같다.</p>
<p><img src="https://images.velog.io/images/ggg9_/post/b25f4737-e0b7-479c-8782-435a16a4567f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정규화]]></title>
            <link>https://velog.io/@ggg9_/%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@ggg9_/%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Sat, 06 Feb 2021 09:41:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>정규화란? 테이블을 올바른 형태로 변경하고 분할하는 것을 정규화라 한다.</p>
</blockquote>
<h3 id="제1정규형">제1정규형</h3>
<ul>
<li>관계형 데이터베이스 테이블에는 하나의 셀에 하나의 값만 저장할 수 있다.</li>
<li>반복되는 부분을 세로(행) 방향으로 늘려나간다.</li>
<li>테이블을 분할하여 중복을 제거하고 기본키(열을 묶어 지정할 수 있다)를 지정한다.</li>
</ul>
<h3 id="제2정규형">제2정규형</h3>
<ul>
<li>기본키에 의해 특정(함수종속성)되는 열과 그렇지 않은 열로 나누어 테이블을 분할하여 중복을 제거한다.
👉 부분 함수종속성(기본키의 일부로 특정할 수 있는 데이터)을 찾아내어 테이블을 분할하는 것</li>
</ul>
<h3 id="제3정규형">제3정규형</h3>
<ul>
<li>기본키 이외의 부분에서 테이블을 분할하여 중복을 제거한다.</li>
</ul>
<p>✔️ 테이블을 분할할때는 서로 결합할 수 있도록 기본키를 추가해 분할한다.</p>
<h3 id="정규화의-목적">정규화의 목적</h3>
<ul>
<li>하나의 데이터가 한 곳에 저장되도록 하기 위함이다.
👉 데이터가 변경되어도 한 곳만 변경하면 된다.</li>
<li>정규화를 통해 인덱스의 재구축을 억제할 수 있다.
👉 인덱스가 지정된 열의 데이터가 변경되는 경우 인덱스도 재구축해야 하지만 기본키는 분할된 테이블을 연계하기 위한 내부적인 데이터로 변경될 일이 거의 없다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 설계]]></title>
            <link>https://velog.io/@ggg9_/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@ggg9_/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Sat, 06 Feb 2021 08:19:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>데이터베이스 설계란? 데이터베이스의 스키마 내에 테이블, 인덱스, 뷰 등의 데이터베이스 객체를 정의하는 것을 말한다. 스키마 내에 정의한다는 뜻에서 &#39;스키마 설계&#39; 라고도 한다.</p>
</blockquote>
<h3 id="데이터베이스-설계">데이터베이스 설계</h3>
<ul>
<li>테이블의 이름이나 열, 자료형을 결정한다.</li>
<li>테이블 간의 관계를 생각하면서 여러 테이블을 정의하고 작성한다.</li>
</ul>
<h4 id="논리명과-물리명">논리명과 물리명</h4>
<ul>
<li>물리명 : 데이터베이스에서 사용될 이름으로 실제로 CREATE TABLE에 지정하는 이름을 말한다.
👉 전통적으로 알파벳을 사용하며 데이터베이스 시스템에 따라 길이 제한이나 공백을 사용할 수 없는 등의 제약이 따른다.</li>
<li>논리명 : 테이블의 설계상 이름에 해당한다.</li>
</ul>
<p>✔️ 물리명은 변경하기 힘들지만 논리명은 언제나 바꿀 수 있다.</p>
<h4 id="자료형">자료형</h4>
<ul>
<li>&#39;1, 2, 3 중 하나&#39; 또는 &#39;yes, no 중 하나&#39;만 취급하는 경우 데이터베이스 기능으로 CHECK 제약을 걸 수 있으므로 데이터 정합성이 중요한 부분에는 적극적으로 사용할 필요가 있다.
👉 &#39;1, 2, 3&#39;의 경우 각 숫자에 &#39;상, 중, 하&#39;와 같은 의미가 부여될때는 정의서의 비고란에 기재한다. 
<code>Tip</code> MySQL에서는 테이블 작성시 comment 키워드를 이용하여 주석을 열 단위로 기입할 수 있다.</li>
</ul>
<h4 id="고정길이와-가변길이">고정길이와 가변길이</h4>
<ul>
<li>자리수가 이미 정해져 있는 경우 고정길이 문자열로 지정한다.</li>
<li>LOB(Large object)형의 경우 큰 데이터를 다루는 자료형이지만 인덱스를 지정할 수 없다.</li>
</ul>
<h4 id="기본키">기본키</h4>
<ul>
<li>MySQL의 경우 AUTO_INCREMENT를 지정하면 INSERT 될 때 마다 번호가 자동증가 된다.
👉 PRIMARY KEY 또는 UNIQUE로 유일성을 지정해야 한다.</li>
</ul>
<h3 id="erentity-relationship다이어그램">ER(Entity Relationship)다이어그램</h3>
<ul>
<li>개체 간 관계를 표현한 것으로 관계형 데이터베이스의 릴레이션과 달리 릴레이션십을 말한다.</li>
<li>릴레이션십을 표기할 때는 숫자나 기호로 나타낼 수 있다. (카디널리티 또는 다중도라고 한다)
👉 가장 기본이 되는 표기방법으로 <strong>일대일, 일대다, 다대다</strong>가 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP]]></title>
            <link>https://velog.io/@ggg9_/HTTP</link>
            <guid>https://velog.io/@ggg9_/HTTP</guid>
            <pubDate>Sun, 17 Jan 2021 10:43:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>HTTP(Hyper Transfer Protocol)란? 웹상에서 서로 다른 서버 간 하이퍼텍스트 문서(HTML)을 주고받을 수 있도록 만들어진 통신 규약이다. 이러한 통신 형식을 프로토콜(protocol)이라 하고 가장 널리 사용되는 프로토콜이 HTTP다.</p>
</blockquote>
<h2 id="통신-방식">통신 방식</h2>
<h3 id="http-요청과-응답">HTTP 요청과 응답</h3>
<ul>
<li>HTTP는 요청(request)과 응답(response)의 구조로 되어있다.
👉 클라이언트가 HTTP 요청을 서버에 보내면 서버는 요청 처리 후 결과에 따른 HTTP 응답을 클라이언트에게 보냄으로써 하나의 HTTP 통신이 된다.</li>
</ul>
<h3 id="stateless">Stateless</h3>
<ul>
<li>상태(state)가 없다는 뜻으로 HTTP 통신에서는 상태라는 개념이 존재하지 않는다.</li>
<li>각각의 HTTP 통신은 독립적이며 그 전에 처리된 HTTP 통신에 대해 전혀 알지 못한다.</li>
</ul>
<h4 id="장점">장점</h4>
<ul>
<li>서버 디자인이 간단해지고 효과적이다.
👉 HTTP 통신들의 상태를 서버에서 저장할 필요가 없으므로 다른 HTTP 통신간의 진행 및 연결 상태의 처리나 저장을 구현하고 관리하지 않아도 된다.
✔️ 각각의 HTTP 요청에 대해 독립적인 응답만 보내주면 된다.</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>해당 요청을 처리하기 위해 필요한 모든 데이터를 매번 포함시켜 요청해야 한다.
👉 예를 들어 새로운 요청에 사용자의 로그인 여부를 포함시켜 보내야 한다면 클라이언트가 사용자의 로그인 사실 여부를 기억하고 있어야 되므로 쿠키(cookie)나 세션(session) 등을 사용하여 요청을 처리할 때 필요한 진행 과정이나 데이터를 저장한다.</li>
</ul>
<p><code>쿠키</code> </p>
<ul>
<li>웹 브라우저(클라이언트 측)는 쿠키라고 하는 파일을 사용하여 웹사이트에서 보내온 필요한 정보를 저장한다.</li>
</ul>
<p><code>세션</code></p>
<ul>
<li>웹 서버에서 필요한 데이터를 저장한다.</li>
</ul>
<h2 id="요청-구조">요청 구조</h2>
<h3 id="start-line">Start Line</h3>
<pre><code class="language-sql"># search 엔드포인트에 GET HTTP 요청
ex. GET /search HTTP/1.1</code></pre>
<h4 id="http-메소드">HTTP 메소드</h4>
<ul>
<li>해당 HTTP 요청이 의도하는 액션을 정의하는 부분이다.</li>
<li>POST, GET, PATCH, PUT, DELETE, OPTIONS 등 여러 메소드들이 있다.</li>
</ul>
<h4 id="request-target">Request target</h4>
<ul>
<li>해당 HTTP 요청이 전송되는 목표 주소를 말한다.</li>
</ul>
<h4 id="http-version">HTTP version</h4>
<ul>
<li>HTTP 버전에는 1.0, 1.1, 2.0이 있다.</li>
<li>버전에 따라 요청 메시지의 구조나 데이터가 다를 수 있으므로 버전을 명시한다.</li>
</ul>
<h3 id="헤더">헤더</h3>
<pre><code class="language-python"># google.com에 보내는 HTTP 요청의 Host 헤더
ex. HOST : google.com</code></pre>
<ul>
<li>HTTP 요청 그 자체에 대한 정보를 담고 있다.
👉 <code>ex.</code> 요청 메시지의 전체 크기(Content-length)</li>
<li>파이썬의 딕셔너리처럼 Key:Value로 되어 있다.</li>
</ul>
<h4 id="host">Host</h4>
<ul>
<li>요청이 전송되는 target 호스트의 URL 주소를 알려주는 헤더.</li>
</ul>
<h4 id="user-agent">User-Agent</h4>
<ul>
<li>요청을 보내는 클라이언트에 대한 정보로 예를 들면 웹 브라우저에 대한 정보.</li>
</ul>
<h4 id="accept">Accept</h4>
<ul>
<li>해당 요청이 받을 수 있는 응답 body 데이터 타입을 알려주는 헤더.</li>
<li>MIME(Multipurpose Internet Mail Extensions) type이 value로 지정된다. <a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types">참고👀</a>
👉 JSON 데이터 타입을 요청하는 경우 application/json MIME type을 value로 정해 주고 모든 데이터 타입을 허용하는 경우 <code>*/*</code>로 지정해주면 된다. </li>
</ul>
<h4 id="connection">Connection</h4>
<ul>
<li>해당 요청이 끝난 후 클라이언트와 서버가 네트워크 연결을 유지할 것인지에 대해 알려 주는 헤더.
👉 HTTP 통신에서 서버 간 네트워크 연결 과정이 다른 작업에 비해 시간이 걸리므로 요청이 계속되는 한 처음 만든 연결을 재사용하는 것이 선호된다.</li>
<li>connection 헤더의 값이 <code>keep-alive</code> 이면 유지, <code>close</code> 이면 닫아도 된다는 뜻이다.</li>
</ul>
<h4 id="content-type">Content-Type</h4>
<ul>
<li>HTTP 요청이 보내는 메시지 body의 타입을 알려주는 헤더.</li>
<li>Accept 헤더와 마찬가지로 MIME type이 사용된다.</li>
</ul>
<h4 id="content-length">Content-Length</h4>
<ul>
<li>HTTP 요청이 보내는 메시지 body의 총 사이즈를 알려 주는 헤더.</li>
</ul>
<h3 id="body">Body</h3>
<ul>
<li>HTTP 요청이 전송하는 데이터를 담고 있는 부분으로 전송하는 데이터가 없다면 비어 있게 된다.</li>
</ul>
<h2 id="응답-구조">응답 구조</h2>
<h3 id="status-line">Status Line</h3>
<pre><code class="language-python">ex. HTTP/1.1 404 Not Found</code></pre>
<ul>
<li>응답 메시지의 상태를 요약하여 알려 주는 부분이다.</li>
</ul>
<h4 id="http-version-1">HTTP version</h4>
<ul>
<li>start line과 마찬가지로 사용되고 있는 버전을 나타낸다.</li>
</ul>
<h4 id="status-code">Status code</h4>
<ul>
<li>응답 상태를 미리 지정되어 있는 숫자로 된 코드로 나타낸다.</li>
</ul>
<h4 id="status-text">status text</h4>
<ul>
<li>응답 상태를 간략하게 글로 설명해 주는 부분이다.</li>
</ul>
<h3 id="헤더-1">헤더</h3>
<ul>
<li>HTTP 요청의 헤더 부분과 동일하지만 응답에서만 사용되는 헤더 값들이 있다.
👉 <code>ex.</code> User-Agent 헤더 대신 Server 헤더가 사용된다.</li>
</ul>
<h3 id="body-1">Body</h3>
<ul>
<li>HTTP 요청 메시지의 body와 동일하다. 전송하는 데이터가 없다면 비어 있게 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 관계형 모델]]></title>
            <link>https://velog.io/@ggg9_/SQL-%EA%B4%80%EA%B3%84%ED%98%95-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@ggg9_/SQL-%EA%B4%80%EA%B3%84%ED%98%95-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Wed, 13 Jan 2021 10:49:59 GMT</pubDate>
            <description><![CDATA[<h2 id="관계형-모델">관계형 모델</h2>
<ul>
<li>관계형 모델(Relational Model)의 기본적인 요소는 릴레이션(Relation)으로 SQL에서 말하는 <strong>테이블</strong>에 해당된다. 
👉 테이블끼리의 관계 아님 ! </li>
<li>관계형 모델은 데이터 구조에 관해 정의한다.</li>
</ul>
<p><code>속성(attribute)</code> </p>
<ul>
<li>속성 이름과 형 이름으로 구성된다.    </li>
<li>SQL의 열에 해당한다.</li>
</ul>
<p><code>튜플(tuple)</code></p>
<ul>
<li>SQL의 행에 해당한다.</li>
</ul>
<h3 id="관계대수">관계대수</h3>
<ul>
<li>릴레이션은 튜플의 집합이며, 릴레이션에 대한 연산이 집합의 대한 연산에 대응 된다.<h4 id="✔️-관계-대수의-기본-규칙">✔️ 관계 대수의 기본 규칙</h4>
<ul>
<li>하나 이상의 관계를 바탕으로 연산한다.</li>
<li>연산한 결과와 반환되는 것 또한 관계이다.
👉 UNION이나 테이블의 결합처럼 연산한 결과도 관계(릴레이션 = 테이블)</li>
<li>연산을 중첩 구조로 실행해도 상관없다.</li>
</ul>
</li>
</ul>
<h3 id="관계형-모델과-sql">관계형 모델과 SQL</h3>
<ul>
<li>관계대수에서는 릴레이션의 연산 방법을 몇 가지 규정한다.</li>
</ul>
<h4 id="합집합">합집합</h4>
<pre><code>mysql&gt; select * from sample71_a;
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)

mysql&gt; select * from sample71_b;
+------+
| b    |
+------+
|    2 |
|   10 |
|   11 |
+------+
3 rows in set (0.00 sec)

mysql&gt; select * from sample71_a UNION select * from sample71_b;
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
|   10 |
|   11 |
+------+
5 rows in set (0.00 sec)</code></pre><ul>
<li>릴레이션끼리의 덧셈</li>
<li>SQL의 UNION</li>
</ul>
<h4 id="차집합">차집합</h4>
<ul>
<li>MySQL🙅🏻</li>
<li>릴레이션끼리의 뺄셈</li>
<li>SQL의 EXCEPT</li>
</ul>
<h4 id="교집합">교집합</h4>
<ul>
<li>MySQL🙅🏻</li>
<li>릴레이션끼리의 공통부분</li>
<li>SQL의 INTERSECT</li>
</ul>
<h4 id="곱집합">곱집합</h4>
<pre><code>mysql&gt; select * from sample71_a, sample71_b;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    2 |    2 |
|    3 |    2 |
|    1 |   10 |
|    2 |   10 |
|    3 |   10 |
|    1 |   11 |
|    2 |   11 |
|    3 |   11 |
+------+------+
9 rows in set (0.00 sec)

mysql&gt; select * from sample71_a CROSS JOIN sample71_b;
+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    2 |    2 |
|    3 |    2 |
|    1 |   10 |
|    2 |   10 |
|    3 |   10 |
|    1 |   11 |
|    2 |   11 |
|    3 |   11 |
+------+------+
9 rows in set (0.00 sec)</code></pre><ul>
<li>릴레이션끼리의 대전표를 조합하는 연산을 말한다.</li>
<li>SQL에서는 FROM 구에 복수의 테이블을 지정한 경우 곱집합으로 계산된다.
👉 CROSS JOIN으로 교차결합을 하면 곱집합을 구할 수 있다.</li>
</ul>
<h4 id="선택">선택</h4>
<ul>
<li>튜플의 추출을 말하며 제한이라 불리기도 한다.</li>
<li>SQL의 WHERE 구에 조건을 지정하여 검색하는 것에 해당된다.
👉 튜플은 SQL에서 행을 말하기 때문</li>
</ul>
<h4 id="투영">투영</h4>
<ul>
<li>속성의 추출을 말한다.</li>
<li>SQL의 SELECT 구에 결과로 반환할 열을 지정하는 것에 해당된다.</li>
</ul>
<h4 id="결합">결합</h4>
<ul>
<li>릴레이션끼리 교차결합하여 계산된 곱집합에서 결합조건을 만족하는 튜플을 추출하는 연산이다.</li>
<li>SQL의 내부결합</li>
<li>관계대수에도 내부결합과 외부결합이 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 뷰 작성 & 삭제]]></title>
            <link>https://velog.io/@ggg9_/SQL-%EB%B7%B0-%EC%9E%91%EC%84%B1-%EC%82%AD%EC%A0%9C</link>
            <guid>https://velog.io/@ggg9_/SQL-%EB%B7%B0-%EC%9E%91%EC%84%B1-%EC%82%AD%EC%A0%9C</guid>
            <pubDate>Sun, 03 Jan 2021 10:09:46 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>뷰란? 테이블과 같은 부류의 데이터베이스 객체 중 하나로 FROM 구에 기술된 서브쿼리에 이름을 붙이고 데이터베이스 객체화하여 쓰기 쉽게 한 것을 뷰라고 한다. 즉, 뷰는 SELECT 명령을 기록하는 데이터베이스 객체다.</p>
</blockquote>
<h3 id="뷰">뷰</h3>
<ul>
<li>뷰를 참조하면 정의된 SELECT 명령의 실행결과를 테이블처럼 사용할 수 있다.</li>
<li>서브쿼리 부분을 &#39;뷰 객체&#39;로 만들면 SELECT 명령을 간략하게 표현할 수 있고 데이터베이스에 등록해 두었다가 간단히 실행할 수도 있다.</li>
</ul>
<h4 id="가상-테이블">가상 테이블</h4>
<ul>
<li>뷰는 테이블처럼 취급할 수 있지만 데이터를 쓰거나 지울 수 있는 저장공간을 가지지 않기 때문에 <strong>가상 테이블</strong>이라 불리기도 한다.
👉 이 때문에 SELECT 명령에서만 사용하는 것을 권장한다.</li>
</ul>
<h3 id="뷰-작성과-삭제">뷰 작성과 삭제</h3>
<ul>
<li>뷰는 데이터베이스 객체이기 때문에 DDL로 작성하거나 삭제한다.</li>
</ul>
<h4 id="뷰-작성">뷰 작성</h4>
<pre><code class="language-sql">CREATE VIEW 뷰명 AS SELECT 명령</code></pre>
<pre><code>mysql&gt; CREATE VIEW sample_view_67 AS SELECT * FROM sample54;
Query OK, 0 rows affected (0.01 sec)

mysql&gt; SELECT * FROM sample_view_67;
+------+------+
| no   | a    |
+------+------+
|    1 |  900 |
|    2 |  900 |
+------+------+
2 rows in set (0.00 sec)</code></pre><pre><code class="language-sql">CREATE VIEW 뷰명 (열명1, 열명2, ...) AS SELECT 명령</code></pre>
<pre><code>mysql&gt; CREATE VIEW sample_view_672(n,v,v2) AS
    -&gt; SELECT no,a,a*2 FROM sample54;
Query OK, 0 rows affected (0.00 sec)

mysql&gt; SELECT * FROM sample_view_672 WHERE n = 1;
+------+------+------+
| n    | v    | v2   |
+------+------+------+
|    1 |  900 | 1800 |
+------+------+------+
1 row in set (0.00 sec)</code></pre><ul>
<li>AS 키워드는 별명을 붙일 때와는 달리 생략 할 수 없다.</li>
<li>열을 지정할 경우 SELECT 구와 같은 수의 열을 지정해야 한다.
👉 열 이외 자료형이나 제약은 지정할 수 없다.</li>
</ul>
<h4 id="뷰-삭제">뷰 삭제</h4>
<pre><code class="language-sql">DROP VIEW 뷰명</code></pre>
<pre><code>mysql&gt; DROP VIEW sample_view_67;
Query OK, 0 rows affected (0.00 sec)</code></pre><h3 id="뷰의-단점">뷰의 단점</h3>
<ul>
<li>뷰는 데이터베이스 객체로 저장장치에 저장되지만 테이블과 달리 대량의 저장공간을 필요로 하지 않는다.
👉 데이터베이스에 저장되는 것은 SELECT 명령으로 저장공간을 소비하지 않는 대신 CPU 자원을 사용한다.</li>
<li>뷰를 참조하면 뷰에 등록된 SELECT 명령이 실행되고 실행 결과는 일시적으로 보존된다.</li>
</ul>
<h4 id="머티리얼라이즈드-뷰materialized-view">머티리얼라이즈드 뷰(Materialized View)</h4>
<p>🙅🏻 MySQL에서는 사용할 수 없다.. 
✔️ 뷰의 근원 테이블에 보관하는 데이터양이 많은 경우, 뷰를 사용한 집계처리, 뷰를 중첩해서 사용하는 경우 처리 속도가 떨어진다.</p>
<ul>
<li>위와 같은 상황을 회피하기 위해 사용하는 것이 머티리얼라이즈드 뷰이다.</li>
<li>데이터를 일시적으로 저장하여 사용하는 것이 아니라 테이블처럼 저장장치에 저장해두고 사용한다.
👉 일반적인 뷰처럼 SELECT 명령을 실행할 필요가 없다. 
<code>!but</code> 데이터가 변경된 경우 SELECT 명령을 재실행하여 데이터를 다시 저장한다. (RDBMS가 자동으로 실행)</li>
</ul>
<h4 id="함수-테이블">함수 테이블</h4>
<p>✔️ 뷰를 구성하는 SELECT 명령은 단독으로 실행할 수 있어야 하기 때문에 상관 서브쿼리는 사용할 수 없다.</p>
<ul>
<li>함수 테이블(테이블을 결괏값으로 반환해주는 사용자정의 함수)의 함수에는 인수를 지정할 수 있기 때문에 인수의 값에 따라 WHERE 조건을 붙여 결괏값을 바꾸면 상관 서브쿼리처럼 동작할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 인덱스 작성 & 삭제]]></title>
            <link>https://velog.io/@ggg9_/SQL-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%9E%91%EC%84%B1%EA%B3%BC-%EC%82%AD%EC%A0%9C</link>
            <guid>https://velog.io/@ggg9_/SQL-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EC%9E%91%EC%84%B1%EA%B3%BC-%EC%82%AD%EC%A0%9C</guid>
            <pubDate>Sat, 02 Jan 2021 07:08:41 GMT</pubDate>
            <description><![CDATA[<h2 id="인덱스">인덱스</h2>
<pre><code class="language-sql">CREATE INDEX
DROP INDEX</code></pre>
<ul>
<li>인덱스는 데이터베이스 객체의 하나로 DDL을 사용하여 작성하거나 삭제한다.
👉 표준 SQL 명령은 없지만 대표적인 데이터베이스 제품에는 인덱스 구조가 도입되어 있으며 비슷한 관리 방법으로 인덱스를 다룰 수 있다.</li>
</ul>
<h3 id="인덱스-작성">인덱스 작성</h3>
<pre><code class="language-sql">CREATE INDEX 인덱스명 ON 테이블명(열명1, 열명2, ...)</code></pre>
<pre><code># sample62 테이블의 no 열에 isample65 인덱스 지정
mysql&gt; CREATE INDEX isample65 ON sample62(no);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; SHOW INDEX FROM sample62;
+----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table    | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| sample62 |          1 | isample65 |            1 | no          | A         |           0 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
1 row in set (0.01 sec)</code></pre><ul>
<li>인덱스에 이름을 붙여 관리하는데 데이터베이스 객체가 될지 테이블의 열처럼 취급될지는 제품에 따라 다르다.
👉 SQL server나 MySQL에서는 테이블 내의 객체가 되므로 테이블 내 이름이 중복되지 않도록 지정해야 한다.</li>
<li>해당 인덱스가 어느 테이블의 어느 열에 관한 것인지 지정해야 한다.
👉 복수로도 지정할 수 있다.</li>
<li>인덱스 작성시 색인용 데이터가 생성되고 테이블 크기에 따라 작성 시간도 달라진다.
👉 행이 대량으로 존재하면 시간도, 저장공간도 많이 소비된다.</li>
</ul>
<h3 id="인덱스-삭제">인덱스 삭제</h3>
<pre><code class="language-sql"># 스키마 객체의 경우
DROP INDEX 인덱스명

# 테이블 내 객체의 경우
DROP 인덱스명 ON 테이블명</code></pre>
<pre><code>mysql&gt; DROP INDEX isample65 ON sample62;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; SHOW INDEX FROM sample62;
Empty set (0.00 sec)</code></pre><ul>
<li>인덱스만 삭제하는 경우 DROP INDEX를 사용한다.
👉 인덱스는 테이블에 의존하는 객체이므로 테이블을 삭제하면 인덱스도 자동으로 삭제된다.</li>
<li>인덱스의 열을 WHERE 구로 조건을 지정하여 SELECT 명령으로 검색하면 처리속도가 향상된다.
<code>!but</code> 모든 SELECT 명령에 적용되는 인덱스는 작성할 수 없다.</li>
<li>INSERT 명령의 경우 인덱스를 최신 상태로 갱신하는 처리가 늘어나므로 처리속도가 떨어진다.</li>
</ul>
<h3 id="explain">EXPLAIN</h3>
<pre><code class="language-sql">EXPLAIN SQL명령</code></pre>
<pre><code>mysql&gt; EXPLAIN SELECT * FROM sample62 WHERE a = &#39;a&#39;; 
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type | possible_keys | key       | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | sample62 | NULL       | ref  | isample65     | isample65 | 93      | const |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+------+---------------+-----------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

# 조건식에 a 열이 사용되지 않으면 인덱스를 사용할 수 없다.
mysql&gt; EXPLAIN SELECT * FROM sample62 WHERE c = &#39;a&#39;;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | sample62 | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    1 |   100.00 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)</code></pre><ul>
<li>인덱스를 사용하여 검색하는지를 확인할 때 사용한다.</li>
<li>SQL 명령은 실제로 실행되지는 않지만 MySQL의 경우 실제로 실행되는 경우도 있다.</li>
<li><code>possible_keys</code>에 사용될 수 있는 인덱스, <code>key</code>는 사용된 인덱스가 표시된다.</li>
</ul>
<h3 id="최적화">최적화</h3>
<ul>
<li>내부적으로 SELECT 명령을 실행하기 앞서 <strong>실행계획</strong>을 세운다.
👉 EXPLAIN 명령은 이 실행계획을 확인하는 명령</li>
<li>실행계획에서는 인덱스 사용 여부에 대해서도 데이터베이스 내부의 최적화 처리를 통해 판단한다.
👉 판단 기준으로 인덱스의 품질도 고려하므로 서로 다른 값의 여러 종류의 데이터가 존재하면 그만큼 효율도 좋아진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 인덱스 구조]]></title>
            <link>https://velog.io/@ggg9_/SQL-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@ggg9_/SQL-%EC%9D%B8%EB%8D%B1%EC%8A%A4-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Wed, 30 Dec 2020 11:14:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>인덱스란 ? &#39;색인&#39;이라고도 불리는 인덱스는 데이터베이스 객체 중 하나이다.</p>
</blockquote>
<h3 id="인덱스">인덱스</h3>
<ul>
<li>테이블에 붙여진 <strong>색인</strong>으로 검색속도가 향상된다.</li>
<li>검색 시에 쓰이는 키워드와 대응하는 데이터 행의 장소가 저장되어 있다.</li>
<li>테이블과는 별개로 독립된 데이터베이스 객체로 작성된다.
👉 <code>!but</code> 테이블에 의존하는 객체로 테이블을 삭제하면 인덱스도 함께 삭제된다.</li>
</ul>
<h3 id="검색-알고리즘">검색 알고리즘</h3>
<ul>
<li>대표적으로 <strong>이진 트리</strong>(binary tree)가 있고 다음으로 <strong>해시</strong>가 유명하다.</li>
</ul>
<h4 id="풀-테이블-스캔full-table-scan">풀 테이블 스캔(full table scan)</h4>
<ul>
<li>인덱스가 지정되지 않은 테이블을 검색할 때 사용된다.</li>
<li>테이블에 저장된 모든 값을 처음부터 차례로 조사해나간다.</li>
</ul>
<h4 id="이진-탐색binary-search">이진 탐색(binary search)</h4>
<ul>
<li>차례로 나열된 집합에 유효한 검색 방법으로 처음부터 차례로 조사하는 것이 아닌 집합을 반으로 나누어 조사한다.</li>
</ul>
<p>✔️ <strong>대량의 데이터를 검색할 때는 이진 탐색이 빠르다.</strong></p>
<ul>
<li>풀 테이블 스캔은 데이터 수에 비례해 비교 횟수도 늘어나지만 이진 탐색은 데이터 수가 배가 되어도 비교 횟수는 1회 밖에 늘어나지 않는다.</li>
</ul>
<h4 id="이진-트리binary-tree">이진 트리(binary tree)</h4>
<ul>
<li>테이블에 인덱스를 작성하면 테이블 데이터와 별개로 이진 트리라는 데이터 구조로 인덱스용 데이터가 만들어진다.</li>
<li>트리는 노드(node)라는 요소로 구성된다.
👉 각 노드는 작은 값과 큰 값의 두 개의 가지로 나뉜다.</li>
<li>원하는 수치와 비교하여 더 크면 오른쪽, 작으면 왼쪽 가지를 조사해 나간다.</li>
</ul>
<h3 id="유일성">유일성</h3>
<ul>
<li>이진 트리에서는 집합 내에 중복 값을 가질 수 없다.
<code>!but</code> 같은 값을 가지는 노드를 여러 개 만들 수 없다는 특성은 키에 대해 유일성을 가지게 할 경우만 유용하다.
👉 기본키 제약은 이진 트리로 인덱스를 작성하는 데이터베이스가 많다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] 제약]]></title>
            <link>https://velog.io/@ggg9_/SQL-%EC%A0%9C%EC%95%BD</link>
            <guid>https://velog.io/@ggg9_/SQL-%EC%A0%9C%EC%95%BD</guid>
            <pubDate>Sun, 27 Dec 2020 17:51:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>제약이란? 제약은 테이블에 설정하는 것이다. CREATE로 테이블 생성시 정의하거나 ALTER로 변경할 수 있다.</p>
</blockquote>
<h3 id="테이블-작성시-제약-정의">테이블 작성시 제약 정의</h3>
<pre><code># 열 제약 정의
mysql&gt; CREATE TABLE sample631 (
    -&gt; a INTEGER NOT NULL,
    -&gt; b INTEGER NOT NULL UNIQUE,
    -&gt; c VARCHAR(30)
    -&gt; );
Query OK, 0 rows affected (0.02 sec)

mysql&gt; DESC sample631;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a     | int         | NO   |     | NULL    |       |
| b     | int         | NO   | PRI | NULL    |       |
| c     | varchar(30) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</code></pre><ul>
<li>하나의 열에 대한 제약은 열을 정의할 때 지정한다. 이처럼 열에 대해 정의하는 제약을 <strong>열 제약</strong>이라 부른다.</li>
</ul>
<pre><code># 테이블 제약 정의
mysql&gt; CREATE TABLE sample632 (
    -&gt; no INTEGER NOT NULL,
    -&gt; sub_no INTEGER NOT NULL,
    -&gt; name VARCHAR(30),
    -&gt; PRIMARY KEY(no, sub_no)
    -&gt; );
Query OK, 0 rows affected (0.01 sec)

mysql&gt; DESC sample632;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| no     | int         | NO   | PRI | NULL    |       |
| sub_no | int         | NO   | PRI | NULL    |       |
| name   | varchar(30) | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)</code></pre><ul>
<li>&#39;복수열에 의한 기본키 제약&#39;처럼 한 개의 제약으로 복수 열에 제약을 지정하는 경우를 <strong>테이블 제약</strong>이라 부른다.</li>
</ul>
<pre><code>mysql&gt; CREATE TABLE sample633(
    -&gt; no INTEGER NOT NULL,
    -&gt; sub_no INTEGER NOT NULL,
    -&gt; name VARCHAR(30),
    -&gt; CONSTRAINT pkey_sample PRIMARY KEY (no, sub_no) 👉 제약 이름 지정
    -&gt; );
Query OK, 0 rows affected (0.01 sec)

mysql&gt; DESC sample633;
+--------+-------------+------+-----+---------+-------+
| Field  | Type        | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| no     | int         | NO   | PRI | NULL    |       |
| sub_no | int         | NO   | PRI | NULL    |       |
| name   | varchar(30) | YES  |     | NULL    |       |
+--------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</code></pre><ul>
<li>제약에 이름을 붙이면 관리하기 쉬워지므로 가능한 이름을 붙이도록 한다.</li>
</ul>
<h3 id="제약-추가">제약 추가</h3>
<h4 id="열-제약-추가">열 제약 추가</h4>
<pre><code># c 열에 NOT NULL 제약 걸기
mysql&gt; ALTER TABLE sample631 MODIFY c VARCHAR(30) NOT NULL;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; DESC sample631;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a     | int         | NO   |     | NULL    |       |
| b     | int         | NO   | PRI | NULL    |       |
| c     | varchar(30) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</code></pre><ul>
<li>기존 테이블을 변경할 경우 제약을 위반하는 데이터가 있는지 먼저 검사한다.</li>
</ul>
<h4 id="테이블-제약-추가">테이블 제약 추가</h4>
<pre><code># 기본키 제약 추가
mysql&gt; ALTER TABLE sample631 ADD CONSTRAINT pkey_sample631 PRIMARY KEY(a);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; DESC sample631;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a     | int         | NO   | PRI | NULL    |       |
| b     | int         | NO   | UNI | NULL    |       |
| c     | varchar(30) | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</code></pre><ul>
<li>ALTER TABLE의 ADD 하부명령으로 추가할 수 있다.</li>
<li>기본키는 테이블에 하나만 설정할 수 있다.</li>
</ul>
<h3 id="제약-삭제">제약 삭제</h3>
<pre><code># c 열의 NOT NULL 제약 삭제
mysql&gt; ALTER TABLE sample631 MODIFY c VARCHAR (30);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; DESC sample631;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a     | int         | NO   | PRI | NULL    |       |
| b     | int         | NO   | UNI | NULL    |       |
| c     | varchar(30) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</code></pre><pre><code># 기본키 제약 삭제
mysql&gt; ALTER TABLE sample631 DROP PRIMARY KEY;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; DESC sample631;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| a     | int         | NO   |     | NULL    |       |
| b     | int         | NO   | PRI | NULL    |       |
| c     | varchar(30) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)</code></pre><ul>
<li>기본키는 테이블당 하나만 설정할 수 있기 때문에 제약명을 지정하지 않고도 삭제할 수 있다.</li>
</ul>
<h3 id="기본키">기본키</h3>
<pre><code>mysql&gt; CREATE TABLE sample634(
    -&gt; p INTEGER NOT NULL,
    -&gt; a VARCHAR (30),
    -&gt; CONSTRAINT pkey_sample634 PRIMARY KEY(p)
    -&gt; );
Query OK, 0 rows affected (0.01 sec)

mysql&gt; DESC sample634;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| p     | int         | NO   | PRI | NULL    |       |
| a     | varchar(30) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)</code></pre><pre><code>mysql&gt; INSERT INTO sample634 VALUES (3, &#39;셋째줄&#39;);
Query OK, 1 row affected (0.01 sec)

mysql&gt; SELECT * FROM sample634;
+---+-----------+
| p | a         |
+---+-----------+
| 1 | 첫째줄      |
| 2 | 둘째줄      |
| 3 | 셋째줄      |
+---+-----------+
3 rows in set (0.00 sec)

mysql&gt; INSERT INTO sample634 VALUES (2, &#39;넷째줄&#39;);
ERROR 1062 (23000): Duplicate entry &#39;2&#39; for key &#39;sample634.PRIMARY&#39;

mysql&gt; UPDATE sample634 SET p = 2 WHERE p = 3;
ERROR 1062 (23000): Duplicate entry &#39;2&#39; for key &#39;sample634.PRIMARY&#39;</code></pre><ul>
<li>기본키로 지정할 열은 NOT NULL 제약이 설정되어 있어야 한다.</li>
<li>기본키는 테이블의 행 하나를 특정할 수 있는 검색키이다.
👉 기본키로 설정된 열이 중복하는 데이터 값을 가지면 제약에 위반된다.</li>
<li>열을 기본키로 지정해 유일한 값을 가지게 하는 구조가 <strong>기본키 제약</strong>이다.
👉 행이 유일성을 필요로 한다는 의미에서 &#39;유일성 제약&#39;이라 불리기도 한다.</li>
</ul>
<h4 id="복수-열로-기본키-구성하기">복수 열로 기본키 구성하기</h4>
<pre><code>mysql&gt; SELECT a, b FROM sample635;
+---+---+
| a | b |
+---+---+
| 1 | 1 | 👉 a 열만 봤을 때는 중복 값이 있지만 b 열이 다르기때문에 키 전체로서는 중복되지 않는다.
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+---+---+
5 rows in set (0.01 sec)</code></pre><ul>
<li>키를 구성하는 모든 열을 사용해서 중복 값이 있는지를 검사한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>