<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>VLV's blog</title>
        <link>https://velog.io/</link>
        <description>커피, IT기기, 노래를 좋아해요.</description>
        <lastBuildDate>Tue, 25 Jul 2023 09:11:50 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>VLV's blog</title>
            <url>https://velog.velcdn.com/images/vlv_3/profile/434a9be5-f264-4e1b-9827-6868370ab9a9/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. VLV's blog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/vlv_3" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[what's the strong point when we use RDB(like postgresql) and ElasticSearch Concurrently? is there a good time to use this architecture? ]]></title>
            <link>https://velog.io/@vlv_3/whats-the-strong-point-when-we-use-RDBlike-postgresql-and-ElasticSearch-is-there-a-good-time-to-use-this-architecture</link>
            <guid>https://velog.io/@vlv_3/whats-the-strong-point-when-we-use-RDBlike-postgresql-and-ElasticSearch-is-there-a-good-time-to-use-this-architecture</guid>
            <pubDate>Tue, 25 Jul 2023 09:11:50 GMT</pubDate>
            <description><![CDATA[<p>Relational Database Management Systems (RDBMS) like PostgreSQL and NoSQL databases like Elasticsearch have their own unique advantages and are used for specific types of tasks based on their strengths.</p>
<p>PostgreSQL (RDBMS):</p>
<p>ACID Compliance: PostgreSQL provides Atomicity, Consistency, Isolation, Durability (ACID) properties, which ensure reliable processing of transactions.
Structured Data: PostgreSQL is excellent for managing structured data with complex relations.
SQL Queries: PostgreSQL uses SQL for querying, which is a powerful and standardized language for database management and data manipulation.
Integrity Constraints: PostgreSQL supports integrity constraints like primary key, foreign key, ensuring data consistency and accuracy.
Support for complex operations: PostgreSQL can handle complex operations and computations, such as joins and subqueries, which might not be possible in a NoSQL solution like Elasticsearch.
Elasticsearch (NoSQL):</p>
<p>Full-text Search: Elasticsearch is renowned for its full-text search capabilities. It provides fast, real-time search functionalities.
Scalability: Elasticsearch is designed to scale horizontally. It&#39;s easy to scale out by adding more nodes to the system.
Real-Time Analysis: Elasticsearch is excellent for log or event data analysis, allowing for real-time insights.
Flexible Schema: Elasticsearch can handle semi-structured and unstructured data effectively. It&#39;s suitable for diverse forms of data.
Geo-Spatial functions: Elasticsearch supports complex geo-spatial queries like distance and location-based searches.
In terms of architecture, a common pattern is to use both of these technologies in tandem:</p>
<p>Transactional and Complex Queries: You would use PostgreSQL to manage transactional data, handle complex queries, and manage data integrity.
Search and Analytics: You would use Elasticsearch for text search, data analysis, logging, and scenarios where you need fast, real-time insights.
This combined architecture can be beneficial in use-cases like an e-commerce application where PostgreSQL can handle transactional operations (like user info, orders, payments) and Elasticsearch can power the product search and analytics functionality.</p>
<p>As a rule of thumb, you should consider the needs and requirements of your specific project to decide whether this architecture is right for you. For instance, if your application heavily relies on text search and analysis but also needs to handle complex relations and ACID properties, combining PostgreSQL and Elasticsearch can be a very effective solution.</p>
<h2 id="is-there-a-tool-for-ensuring-data-consistency-between-elasticsearch-and-rdb">is there a tool for Ensuring data consistency between elasticSearch and RDB</h2>
<p>Ensuring data consistency between two separate data stores like Elasticsearch and a relational database (RDB) like PostgreSQL can be challenging because they work in fundamentally different ways. There are however a few strategies and tools you could use:</p>
<p>Dual writes: One strategy is to write data to both systems at the same time. However, this can lead to inconsistency if one write fails and the other succeeds. This could be mitigated by using an atomic commit protocol (two-phase commit), although this adds complexity and can affect performance. It&#39;s also worth noting that Elasticsearch doesn&#39;t provide the same consistency guarantees as PostgreSQL, so there could still be temporary inconsistencies even if both writes succeed.</p>
<p>Change Data Capture (CDC): Change data capture (CDC) is a technique that captures changes made at the data source and applies them to the target systems. Tools like Debezium can capture changes in your RDB and push them to a message queue system like Apache Kafka. From there, changes can be consumed by a separate process and applied to Elasticsearch. This ensures that both systems have the same data without the need for dual writes or atomic commit protocols.</p>
<p>Logstash: Logstash, part of the ELK stack (Elasticsearch, Logstash, Kibana), can also be used to import data from RDBs into Elasticsearch. While this is more often used for the initial import of data, it could also be used in conjunction with a job scheduler to regularly sync data.</p>
<p>Custom Application Logic: You can write your own application logic that writes to the RDB first and then propagates those changes to Elasticsearch. In the event of failure, your application can implement retry logic, or flag the data for a future reconciliation process.</p>
<p>Remember, there&#39;s no silver bullet to maintain consistency across different data stores. The best strategy depends on your specific use-case, requirements, and the resources at your disposal. You also need to consider how much inconsistency (if any) your application can tolerate, and how you plan to handle failures when they occur.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실전프로젝트 4주차 회고: koBERT기반 MLC 모델 배포 및 캐시 서버 구현]]></title>
            <link>https://velog.io/@vlv_3/%EC%8B%A4%EC%A0%84%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-koBERT%EA%B8%B0%EB%B0%98-MLC-%EB%AA%A8%EB%8D%B8-%EB%B0%B0%ED%8F%AC-%EB%B0%8F-%EC%BA%90%EC%8B%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@vlv_3/%EC%8B%A4%EC%A0%84%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-4%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-koBERT%EA%B8%B0%EB%B0%98-MLC-%EB%AA%A8%EB%8D%B8-%EB%B0%B0%ED%8F%AC-%EB%B0%8F-%EC%BA%90%EC%8B%9C-%EC%84%9C%EB%B2%84-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Sun, 04 Dec 2022 05:41:55 GMT</pubDate>
            <description><![CDATA[<p>금주의 성과는 데이터 전처리 코드 개선으로 인공지능 모델의 accuracy를 0.78 -&gt; 0.92까지 향상시킨 점, 인공지능 모델 배포 후 기존에 구상했던 모든 서버를 구현, 연결하여 부하 테스트를 성공했다는 점과, AI모델이 올라가있는 서버를 덜 쓰기 위해 구상했던 캐시 서버의 개선된 버전을 온라인에 배포했다는 것이다. 데이터 흐름의 초기 구상은 다음과 같았다. 
<img src="https://velog.velcdn.com/images/vlv_3/post/1f9d6ad1-e249-419b-af70-38dc4d2ef8db/image.png" alt="데이터 흐름">
서비스 사용자로부터 외국어로 된 증상을 입력받으면, 캐시서버는 번역 API로부터 한국어로 번역된 문장을 받는다. 이 번역된 문장은 AI 서버로 향하고, AI 서버는 전처리 및 토큰화된 wordset을 반환한다. 캐시 서버는 내부 Q&amp;A 테이블을 wordset으로 검색하는데, Q&amp;A 테이블에 저장된 wordset과 검색하기 위한 wordset의 일치도가 가장 높은 Question의 Answer를 반환한다. 캐시 서버의 Q&amp;A wordset은 서비스 사용자의 성공적인 탐색 사례를 전처리된 단어 집합인 wordset형태로 모두 저장한다. 캐시 서버는 또한 오래된 정보 또는 거의 재탐색되지 않은 정보를 삭제한다. 예를 들어 &quot;목&quot;, &quot;편도&quot;, &quot;열과 통증&quot; 따위의 단어가 검색 wordset과 Q&amp;A 테이블의 Question wordset에 공통되어 있다면, 이 두 set 사이의 일치도가 높을 것이고, 이 Question과 매칭되어 있는 Answer(&quot;이비인후과&quot;)가 반환될 확률도 높아질 것이다. 일치도가 유사한 여러 개의 Question이 탐색되거나 유의한 Answer를 찾지 못한다면, 캐시 서버는 AI 서버에게 Answer를 요구한다. 
<img src="https://velog.velcdn.com/images/vlv_3/post/802b1bcb-e7f6-4813-8646-f84f20f3e44c/image.png" alt="">
그러나 이 방법은 몇 가지 문제가 있다. 첫째, 일치도(또는 질문 사이의 유사한 정도)를 비교하기 위해 Q&amp;A dataset 내부의 모든 Question을 탐색해야 한다. 단 하나의 적합한 정보를 추출하기 위해, 불필요한 n-1번의 검색 결과는 버려야한다. 둘째, 우리 서비스는 모두 캐시서버를 통과하고 있는데, 캐시서버에 과도한 읽기, 쓰기, 삭제 요청이 몰리게 되면 AI서버나 병원 정보를 저장하고 있는 WS가 얼마나 크던 서비스 결과를 받아볼 수 없게 된다. 캐시 서버에 저장된 데이터의 신선도를 보장하기 위해 실시간으로 데이터를 입력받고 삭제하는 것은 캐시 서버에 큰 부담을 줄 수 있다. 셋째, 보다 정확히 두 wordset 사이의 일치도를 계산하기 위하여 더 복잡한 알고리즘, 가령 keyBERT와 같은 AI 모델이 사용될 수 있지만, AI 서버를 덜 쓰기 위하여 다른 AI 모델을 도입하는 것은 모순적이다. </p>
<p>이 문제를 개선하기 위해 캐시 서버를 다음과 같이 개선하였다. 첫째, 미세 조정 학습을 위해 수집한 약 20만개의 네이버 Q&amp;A 증상 데이터로부터 약 30만개의 단어와 14개 분과로 구성된 분과별 TF-IDF 테이블을 만들었다. TF-IDF 테이블은 전처리된 단어 Tokens가 분과 별로 가지는 나이브한 가중치 정보를 내포하고 있다. TF-IDF의 아이디어는 단순한데, 분과 별로 그 Token이 등장한 비율(Term Frequency)과 그 Token이 등장한 분과 수의 역수(Inverse Document Frequency)를 곱한 것이다. 오직 그 분과에서 여러 번 등장하는 Token이라면 TF와 IDF가 커질 것이므로, 더 높은 가중치를 가질 것이다. 그 반대의 경우도 마찬가지이다. 이런 나이브한 가중치를 사용하는 이유 중 하나는 이 TF-IDF 테이블이 주기적(가령 하루, 일주일, 한달 등 긴 기간)으로 update될 것을 가정할 때, 사이 기간이 짧을 수록 서버 부하가 증가하므로, 단순한 연산으로 구성할 수 있되 정확도와 속도를 Trade-off할 수 있다면 좋을 것이라고 판단했기 때문이었다. 둘째, 캐시 서버는 TF-IDF 테이블을 저장하고 있다가, 사용자로부터 증상 데이터를 입력 받으면 AI 서버에 전처리를 요구한다. AI 서버로부터 전처리된 단어 Tokens를 받은 캐시 서버는 내부의 TF-IDF 테이블로부터 이 단어와 매칭된 가중치를 검색한다. 기존에 Q&amp;A 테이블에 저장된 모든 question wordset이 타겟이 된 것과 달리, 오직 Token 개수 만큼의 숫자 데이터를 불러오고 있을 뿐이다. 하나의 단어 Token은 TF-IDF 테이블에서 1x14의 크기를 가지며, 각 분과를 가리키는 방향성과 크기를 TF-IDF 형태의 나이브한 가중치로 가지고 있다. 검색이 끝나면, 분과 별로 가중치의 합을 구한다. 가중치의 합이 가장 큰 분과가 캐시 서버의 Answer가 된다. 이제 이 단순한 알고리즘이 AI 서버가 내놓은 답과 일치할 가능성을 정의하고, 정답일 가능성이 낮다면 AI 서버에서 정답을 구하도록 해야한다. 이를 위해 가중치 합의 분산을 구하도록 했다. 사용자가 입력한 증상 데이터의 길이가 너무 짧거나 긴 경우 발생할 문제를 예방하기 위해, 전처리된 Token 개수로 검색된 가중치의 분산을 나누어준다. 만약 분산값이 충분히 크다면, 사용자가 입력한 문장이 가지는 분과를 향한 방향성과 크기 역시 클 것으로 예상할 수 있다. 반대의 경우라면, 1. 입력받은 정보가 환자의 증상 정보가 아닌 경우(가령 사용자가 증상과 전혀 관련되지 않은 지문을 입력한 경우) 클라이언트에 경고 메시지(&quot;증상을 입력해 주세요&quot; 등)를 전달하면 되고, 2. 증상 정보이지만 분과가 모호한 경우(가령 &quot;외과&quot; 분과와 &quot;성형외과&quot; 분과) AI 서버로 요청을 보내야 한다. AI 서버의 예측이 정답일 확률이 낮다면, 역시 클라이언트로 경고 메시지(&quot;자세히 입력해주세요&quot;, &quot;증상을 입력해주세요&quot; 등) 또는 다른 제안(인간 상담원과 연결 등)를 보내야 한다. 이를 대처하기 위해 AI 서버에 정답률이 낮을 경우의 예외 처리를 추가하였고, 캐시 서버가 내부 테이블을 검색할 때 TF-IDF 테이블에 없는 단어가 포함된 비율을 정의, 10% 이상 단어집에 없는 단어가 포함된 input을 받으면 경고 메시지를 반환하도록 하였다. </p>
<p>개선된 캐시 서버는 약 40개 단어로 이루어진 증상 데이터 하나를 Input 받았을 때 평균 1.45초 정도의 response time을 보였다. 동일 조건에서 AI server를 거치는 경우와 비교해 약 60%에 가까운 정도의 속도 향상을 보였으나, 트래픽이 몰리는 경우, 단어 길이 조정, 분산의 유의한 수준 변경을 통한 정확도-속도 Trade-off 등 다양한 테스팅 조건 하에서 무조건 AI 서버를 통과하는 것 보다 더 나은 속도를 보이면서 상당한 수준의 정확도를 보일 수 있을지 테스트 및 코드 개선을 이번 주 진행할 예정이다. 또 이번 주에 AI 서버를 t2xlarge에서 p2xlarge로 변경하면서 GPU를 사용한 예측이 가능해질 것이므로, 더 개선된 서버 조건에서 부하 및 성능 테스트를 진행할 예정이다. </p>
<p>남은 이슈중 하나는 보안 문제이다. 환자의 증상 정보는 민감정보에 속할 수 있고, 법적 보호 대상에 속하기 때문에 보안 이슈는 서비스 구현에 매우 크게 다가오는 부분인데, 본래 Https 서버들 사이의 커뮤니케이션을 전제하고 있었으나, socketio &quot;unable to verify the first certificate&quot; 에러 때문에 답보 상태에 있다(<a href="https://stackoverflow.com/questions/32248166/socket-io-unable-to-verify-the-first-certificate">https://stackoverflow.com/questions/32248166/socket-io-unable-to-verify-the-first-certificate</a>). stack overflow에 질문을 올려놓은 상태이며, 이번 주 내로 해결해야 할 과제로 잡았다. https 문제가 해결되면 helmet module을 적용할 예정이다. </p>
<p>마지막으로 Docker로 서버를 감싸는 것과 k8s 기반 로드밸런싱, nestjs + MSA 기반 리팩토링 과제가 남아있는데, 이는 상기한 이슈 해결 이후, 마지막 주 과제로 잡고 있다. </p>
<p>잘 마무리지을 수 있기를 빌며.. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실전프로젝트 2주차 회고: 구현 및 내주 개발 방향]]></title>
            <link>https://velog.io/@vlv_3/%EC%8B%A4%EC%A0%84%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-%EA%B5%AC%ED%98%84-%EB%B0%8F-%EB%82%B4%EC%A3%BC-%EA%B0%9C%EB%B0%9C-%EB%B0%A9%ED%96%A5</link>
            <guid>https://velog.io/@vlv_3/%EC%8B%A4%EC%A0%84%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-%EA%B5%AC%ED%98%84-%EB%B0%8F-%EB%82%B4%EC%A3%BC-%EA%B0%9C%EB%B0%9C-%EB%B0%A9%ED%96%A5</guid>
            <pubDate>Sun, 20 Nov 2022 06:05:16 GMT</pubDate>
            <description><![CDATA[<p>우리 조가 지향하는 서비스는 다음과 같은 조건을 만족해야 한다.</p>
<blockquote>
<p>💊 외국어 번역 API를 통해 Client의 병환을 관련 전문가에게 한국어 문장 형태로 제공할 수 있습니다.
 💊 위치 정보 등을 받아 Client에게 맞춤형 서비스를 제공할 수 있습니다. 
💊 Cache Server를 별도로 두어, 자주 입력받는 데이터를 저장함으로써 서버 부하를 줄일 수 있습니다. 
💊 환자의 병환 Description을 입력받으면 관련 분과 명을 Output하도록 Fine-tuning된 KoBERT 모델을 사용합니다. 
💊 Client의 개인정보는 암호화되어 접근할 수 없어야 합니다.</p>
</blockquote>
<p>이번 주는 기능하는 최소 단위의 프로그램 구현에 초점을 맞추었고, 시연 영상 주소는 다음과 같다.</p>
<p><a href="https://www.youtube.com/watch?v=gmdZg50AtJo">항해99 nodejs-백엔드 10조 중간시연</a></p>
<p>이하는 구현 및 서비스 개선 방향에 대해 서술하였다.</p>
<p>첫째, KoBERT 기반 Multi label classificaiton 모델을 구현하였다. 이 모델은 하이닥 Q&amp;A 데이터를 fine-tuning을 위해 14개 진료 과목 모두 합쳐 약 20만개를 사용하였다. hyper-parameter는 SKT-ai KoBERT 모델 공식 문서를 참조하였으며(<a href="https://github.com/SKTBrain/KoBERT/blob/master/scripts/NSMC/naver_review_classifications_pytorch_kobert.ipynb">https://github.com/SKTBrain/KoBERT/blob/master/scripts/NSMC/naver_review_classifications_pytorch_kobert.ipynb</a>), 대부분 그대로 유지하되 input data 크기에 맞게 sequence max-length만 두배로 늘려 학습하였다(64 to 128). 학습은 colab-pro GPU 환경(P100 GPU &amp; 25GB Ram)에서 이루어졌고, 총 학습시간은 약 4시간 30분 소요되었다. </p>
<p>학습 결과, 모델의 train accuracy는 약 0.78, test accuracy는 약 0.70으로, 우리 서비스가 목표로하는 accuracy 최소치인 0.90보다 상당히 낮은 수치를 보였다. accuracy를 높이기 위해 낮은 성능을 보이는 이유에 대해 원인을 찾은 결과, 모델 자체의 문제이거나 label수가 너무 많기 때문이라고 보긴 어려웠다. 동일 모델 및 유사한 hyper-parameters를 이용하여 비슷한 케이스(증상에 대한 자술 등)의 학습 데이터를 통해 미세조정한 다른 팀의 결과물을 보면, 40개가 넘는 라벨(병명 등)에 대해 약 0.90에 가까운 성능을 보인 경우도 있었기 때문이다. 그렇다면 가장 가능성이 높은 것은 데이터의 질 문제일 것이다. 여러 가능성이 있었지만 우리 팀은 가능한 경우를 크게 세 가지로 축소했다. </p>
<p> 첫째, 데이터 전처리의 quaility 문제일 수 있다. 우리가 사용한 미세조정용 학습 데이터에는 하이닥 서비스의 UI(가령 &lt;이전 글&gt; 등 증상 자술이 아닌 불필요 정보들), 이미지 URI등이 섞여 있는 경우가 더러 있었는데, 이는 크롤링 코드를 개선함으로써 해결할 수 있는 문제이다.</p>
<p> 둘째, 학습용 데이터로서 하이닥 Q&amp;A가 부적절한 측면이 존재했을 수 있다. 하이닥 Q&amp;A 서비스는 서비스 이용자로 하여금 자신의 글을 등록하기에 앞서 서비스 이용자가 인터넷 상담을 받기 원하는 진료 과목 및 전문의를 직접 선택할 수 있도록 하고있다. 필수적인 것은 아니지만, 서비스 이용자 측면에서 직접 유명 전문의에게 질의할 수 있다는 것은 하이닥 서비스의 메리트일 것이다. 그러나 이 데이터 날것자체는 &quot;증상을 입력하면 관련 진료 과목 명을 출력해주는 모델&quot;의 학습데이터로 적합하지 않을 수 있다. 서비스 사용자가 자신의 진료 과목을 선택할 때 default값은 &quot;가정의학과&quot;, 전문의는 관련 질의 점수가 높은 순으로 나열해주고 있는데, 서비스 이용자가 자신의 증상과 맞는 진료과목을 추측하기 어려울 때 이 default 진료과목 및 가장 점수가 높은 의사를 선택할 가능성이 있다(대표성 휴리스틱, representative heuristic, 인간이 연산 리소스를 절약하기 위해 &quot;그것&quot;이 아닌 &quot;그것을 대표하는 것&quot;을 선택하려는 경향성 등). 1차병원으로서 광범위한 진료 및 전문 병원으로의 의뢰 업무도 수행할 수 있는 가정의학과를 default로 둔 것은 하이닥 서비스로써는 디테일한 장점일 수 있지만, 의도한 인공지능 모델을 만드는 데 악재일 수 있다. </p>
<p>셋째, 증상에 관한 분과별 Q&amp;A 데이터셋이 전반적으로 갖는 문제일 수 있다. 즉 대중이 이해하는 진료 과목 별 진료 가능 여부, 범주 및 정도와 의사가 이해하는 것이 다르기 때문일 수 있다고 추측했다. &quot;이런 증상에는 병원의 어떤 분과로 가야할까?&quot; 와 같은 질문에 대중은 과거 경험, 주변의 정보, 상식 등을 바탕으로 추론하지만, 분과별 진료 기준은 의사 등 의학자들이 정한 것이다. 가령 대중의 이해하는 증상 별 진료 가능 범주와 실제로 진료 가능한 범주가 넓은 증상인 경우(감기 등), 환자는 어떤 분과로 찾아가도 좋을 것이다(Question 여러개와 Answer가 여러 개가 중첩 matching되는 경우). 또 대중이 이해하는 진료 가능 범주는 좁지만 실제 진료 가능한 범주가 넓은 경우(question 하나에 answer가 여러개인 경우), 대중이 이해하는 진료 가능 범주가 넓지만 실제 진료 가능한 분과는 적은 경우(Question 여러개에 answer가 하나인 경우)도 존재할 수 있다. 질문(증상)과 정답(진료 가능 분과)이1대 1 관계가 아닌 것이다. 보통 이는 오프라인 진료를 통해 의사의 면밀한 검사로 구체화된다. 환자 자신의 증상에 대한 자술을 전제로 하는 데이터가 가지는 한계이다. </p>
<p>위와 같은 추측들 중 마지막 세 번째는 데이터 셋 자체가 가지는 한계이므로, 팀 리소스를 집중해 해결할 문제가 (이번에는)아닐 것이다. 하지만 첫 번째와 두 번째 추측은 그것이 사실인지 검증하고, 우리 팀의 서비스를 개선할 필요가 있다. 따라서 하이닥 Q&amp;A 데이터와 유사한 데이터 소스인 네이버 지식인 Q&amp;A 서비스를 선택, 크롤링 코드를 개선하여 다시 미세조정을 실시하였다. 네이버 지식인 Q&amp;A는 증상만 기술할 수 있을 뿐 서비스 이용자가 관련 분과 및 전문의를 선택할 수 없다. 때문에 서비스 이용자 자신이 아니라, 여러 분과를 전문한 의사들이 각자 환자가 자술한 증상을 보고 답변을 달 수 있다. 이를 보다 보장하기 위해, 서비스 이용자의 증상을 기준으로 하는 것이 아니라 분과별로 답변을 많이 단 전문의의 글을 찾아 질문과 답, 분과를 수집하였다. 그 결과, 같은 모델/hyper-parameters를 기준으로 train accuracy가 약 0.88 이상으로 증가하였다. 하이닥 데이터 기준 train accuracy 0.78 기준으로 약 12~13%의 성능 향상을 보인 것으로, 비록 label 갯수 및 데이터 사이즈가 동일하지는 않더라도, 유의한 성능 향상을 보임을 관찰할 수 있었다. 때문에 이번 주 목표로 네이버 지식인 Q&amp;A 데이터를 하이닥 데이터와 같은 사이즈&amp; 개선된 크롤링 코드로 크롤링한 데이터로 미세조정을 수행할 계획을 세웠다. 또한 기존의 AWS t2 micro/ ubuntu server로는 prediction by GPU가 불가능하므로, AWS SageMaker로 연산용 서버를 새로 구축, 이전할 예정이다.</p>
<p>이번 주에 해결한 중점 목표로 AI 성능 향상과 더불어 DB에 저장된 데이터와 Kakao map DB 사이의 데이터 최신성 보장 issue를 개선하고자 한다. 현재 우리 Web Server DB에 저장된 4만여개 이상의 병원 및 외국어 지원 약국 정보 데이터는 2021~2022년 기준 서울시 공공데이터 및 보건소 자료를 기반으로 한 것으로, Kakao Map측 DB와 시간적으로 다르기 때문에, &quot;우리 서비스에서 추천해 준 병원을 실제로 가보니 폐업한 경우&quot;등의 사건이 발생할 수 있다. 또, &quot;추천 병원의 내 외국어 지원 가능 여부가 달라진 경우&quot;등의 문제도 발생할 수 있을 것이다. 어떻게 우리 병원 정보 DB의 최신성 보장할 수 있을까? Web server DB의 최신성을 보장하는 것은 불가능하므로, 외국어 지원 가능 여부의 변경은 해결할 수 없다. 그러나 폐업 여부는 Kakao Map DB와 Web Server DB 사이의 불일치가 발생할 시 client에게 response하지 않고, 보다 후순위의 추천 병원으로 대체하여 response하는 예외처리를 구현함으로써 Kakao map으로의 추가적인 query 없이 리스크를 낮출 수 있다.  이 최신성 보장 이슈는 서비스의 전반적인 질과 부정확한 정보를 제외하는 것 사이의 이익/리스크 조절의 문제로 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/vlv_3/post/3e99ae12-ed83-4898-9869-85193334ac78/image.png" alt="사용 모듈 최종"></p>
<p>마지막 목표로 nodejs + express로 작성한 현재 코드를 Typescript/Nest.js 기반으로 Refactoring하고자 한다. 우리 조는 서비스에 MSA 아키텍쳐를 기반으로 Docker/k8s 사용한 로드밸런싱을 적용, 서비스 부하 컨트롤 및 안정성 증대를 목표로 잡고있는데, Module단위 architecture 개발에 용이하고 자체적으로 보유한 모듈 범주도 큰 Nest.js가 뒷받침되면 이 과정이 보다 용이할 것이라고 의견을 모았기 때문이다. 또한 이번 주 멘토링 과정에서 지적해주신 부분인 예외처리 코드가 부족한 점을 개선하기 위해 Nest.js에 내장된 테스트 코드 모듈 jest로 코드 짜는 연습을 하면서 예외처리를 공부할 예정이다. 최종 서비스의 모듈 구조는 다음과 같을 것으로 예상하고 있다. </p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[실전프로젝트 1주차 회고: 서비스 기획 및 개발]]></title>
            <link>https://velog.io/@vlv_3/%EC%8B%A4%EC%A0%84%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%ED%9A%8D-%EB%B0%8F-%ED%8C%80-%EC%95%BD%EC%86%8D-%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@vlv_3/%EC%8B%A4%EC%A0%84%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1%EC%A3%BC%EC%B0%A8-%ED%9A%8C%EA%B3%A0-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B8%B0%ED%9A%8D-%EB%B0%8F-%ED%8C%80-%EC%95%BD%EC%86%8D-%EC%A0%95%EC%9D%98</guid>
            <pubDate>Sun, 13 Nov 2022 07:22:39 GMT</pubDate>
            <description><![CDATA[<p>특정한 웹 서비스의 존재 이유는 무엇일까? 사람마다 그 답이 다르겠지만, 나는 기존에 존재하던 서비스를 개선하거나, 존재하지 않는 서비스를 새로 개발함으로써 서비스 이용자의 삶을 윤택하게 하는 것이라고 생각한다. 따라서 새로운 서비스를 기획하는 사람은 자신이 제공할 서비스에 대하여 다음과 같은 질문에 답할 수 있어야 할 것이다.
 첫째, 내 서비스는 어떤 점에서 새로운가? 어떤 서비스가 다른 서비스보다 새롭다면, 같거나 유사한 문제의식을 가진 다른 상용 서비스들(이하 비교군 서비스)이 가진 장점과 단점을 구분할 수 있어야 한다. 또한, 내가 생각하는 서비스가 왜 지금까지 존재하지 않았는지 스스로 질문할 수 있을 것이다. 둘째, 누구를 위한 것인가? 내 서비스의 비교군에 비해 대상자를 좁히거나 넓힐 수 있을까? 또는 전혀 다른 관점에서 사용자를 특정할 수 있을까? 비교군 서비스를 사용할 수 있는 사람들이 그 서비스를 사용하지 않는 이유는 무엇일까? 셋째, 내 서비스가 사용자들의 삶을 어떻게 더 낫게 만들 수 있을까? 윤택하다는 것이 무슨 뜻일까? 비교군 서비스들은 왜 이들의 삶을 더 윤택하게 하지 못했을까? 상기한 질문들은 항해가 끝날 때 까지 계속해서 고민해야 할 부분이다. 진행 상황에 대해 기록을 남김으로써, 항해가 끝났을 때 우리 서비스가 이 질문에 얼마나 답할 수 있는지 스스로 판단하고자 여기에 기준을 남겨놓는다.</p>
<p> 우리는 백엔드 개발 팀으로, 프론트 &amp; 백엔드 협업 팀이 구체적인 서비스를 개발하는 것을 목적으로 하는 것과 달리, 백엔드 사이드에 한정하여 천만건 이상의 데이터 흐름을 통제하는 경험을 하는 것을 목적으로 하고 있다. 그럼에도, 구체적인 서비스를 기획하는 것은 서비스 개선 방향을 특정할 여지가 크다는 점, 우리 팀의 개발에 동기부여를 할 수 있다는 점 등에서 장점이 있을 것이라고 생각했다. </p>
<p> 우리는 NodeJs 기반 &quot;한국어 사용이 어려운 외국인을 위한 병원 탐색 서비스&quot;를 기획하였다. 외국인 여행자 또는 이민자로서 한국어 사용이 어려운 경우를 상정한 서비스로서, 사용자의 현재 위치를 기반으로 특정 외국어가 가능한 근거리의 병원, 또는 약국 정보와 경로를 추천해주는 것을 목적으로 한다. 
 <img src="https://velog.velcdn.com/images/vlv_3/post/2a50efec-6714-460a-b82b-aa2161c19b1e/image.png" alt="">
서비스를 이용하기 위해 로그인을 필요로 하는데, 회원가입시 Optional하게 Personal Informations를 입력하게 하였다. 이 나이, 성별, 한국/사보험 유무 등을 포함하는 이 Option은 외국인이 한국 병원에서 진료를 받기 위해 필요한 정보 중 일부로, 서비스 사용자가 추천 병원으로 이동하는 등의 경우 병원 또는 관련 전문가에게 제공할 목적으로 만들어졌다. 이는 서비스 사용자가 긴급한 경우, 병원 도착 후 문서 작성 시간을 최소화하기 위한 존재 목적도 겸하고 있다. </p>
<p>로그인에 성공하게 되면, 사용자는 자신의 증상(Symtoms)을 사용자가 원하는 언어로 입력할 수 있다. 서비스가 지원하는 언어는 영어, 태국어, 중국어 등 8개 언어로 한정하였는데, 이는 2021년 기준 새로 진료받은 외국인 환자의 국적을 기준으로 Top 8을 고른 것이다(<a href="https://www.korea.kr/news/pressReleaseView.do?newsId=156513354">https://www.korea.kr/news/pressReleaseView.do?newsId=156513354</a>). 이는 약 80~90%의 외국인 환자의 언어를 커버할 수 있는 범주이다. </p>
<p>증상과 더불어, 사용자가 필요로하는 의료 수준(Necessary Medical Measures)과 긴급한 수준(Emergency Level)을 카테고리에서 선택하도록 했다. 이 정보는 DB에 저장된 병원 리스트로부터 추천 우선순위를 나눌 때 사용될 수 있다. 가령 사용자가 병원 진료가 필요한 것이 아니라 약 처방만으로 충분하고, 긴급 수준도 낮다면, 우리 서비스는 근거리에 위치한 외국어가 가능한 약국을 추천해 줄 수 있다. 반대의 경우라면 관련 병원을 추천해 줄 수 있을 것이다. </p>
<p>이렇게 증상, 필요로 하는 의료 수준 및 긴급 수준을 입력하면 Socket.io로 구현된 ChatRoom을 통해 Bot이 관련 정보를 종합 및 처리하여 가장 적합한 병원 정보를 반환할 수 있다. 사용자가 반환받는 병원 정보로는 병원의 이름, 운영 시간(사용 당시 운영시간이 아니라면 반환하지 않는다), 분과 명(Divisions. 내과, 가정의학과, etc...), 전화번호, 우리 서비스 사용자가 입력한 언어의 한국어 변역(using AWS Translator API), 사용자의 현재 위치로부터 추천 병원까지 최단 경로 등이다. 경로 탐색을 위해 Kakao API를 사용하였다. </p>
<p>단순히 자신의 증상을 입력한 것 만으로 관련 병원 정보를 정확히 탐색하기 어렵기 때문에, KoGTP2 기반 multi-classification 모델을 달아, Symptoms를 입력받으면 관련 분과 명(Division)을 반환하도록 하였다. 모델의 fine-tuning을 위해 하이닥 Q&amp;A (<a href="https://www.hidoc.co.kr/healthqna/list)%EC%99%80">https://www.hidoc.co.kr/healthqna/list)와</a> 네이버 지식인으로부터 환자의 증상 및 담당 의사의 분과 명을 수집하였다. 하이닥 Q&amp;A의 경우 데이터 편향성이 존재할 수 있다는 지적을 한 팀원으로부터 받을 수 있었는데, 하이닥 Q&amp;A의 경우 환자가 증상에 대해 질문글을 올릴 때 진료과 및 답변을 받을 전문의를 직접 선택할 수 있도록 하고 있기 때문이다. 하이닥 서비스 이용자는, 자신의 질환에 대한 충분한 이해가 있다면, 관련 질환의 유명 전문의로부터 직접 답변을 받을 수 있다는 장점이 있겠지만, 학습 데이터로는 적합하지 않을 수 있다. 대중의 질환에 대한 이해와 의사의 이해가 반드시 일치하지 않을 수 있기 때문인데, 가령 자신의 질환에 대해 이해가 부족한 환자는 관련 분과보다 가장 먼저 보이는 진료과목 또는 점수(하이닥 스코어)가 높은 의사를 지명할 가능성이 있다. 하이닥 Q&amp;A를 통해 질문할 때 선택할 수 있는 진료과목의 default는 가정의학과인데, 하이닥 Q&amp;A에 등록된 진료과 별 질문 수 중 가장 Q&amp;A가 많이 등록된 진료과목 역시 가정의학과(22년 11월 13일 현재 기준 약 41000개, 총 약 28만건)인데, 이는 약 2만 4천건이 등록된 정형외과보다 1.5배 이상 높고, 가장 등록된 질문수가 낮은 알레르기 내과(약 1천여건)보다 40배 이상 높은 질문 수이다. 이는 가정의학(Family Midicine)이 1차 병원으로서 진료 범위가 넓은 것을 감안하더라도 상당히 큰 격차인데, 정보 선택에 있어서 대중적 편향(질문군을 대표하는 분과를 선택하려는 대표성 휴리스틱Representative heuristic)이 개입한 결과로 볼 여지가 존재할 가능성이 있다. 따라서 환자가 증상에 대해 질문할 때 진료과목을 선택하지 않고, 대신 의사가 질문을 보고 답변을 달지 선택하는 네이버 지식인 Q&amp;A 서비스로부터 Fine-tuning data를 추가로 crawling하기로 했다. 하이닥 데이터는 크롤링이 거의 완료된 상황인데, 네이버 지식인 데이터를 사용한 경우와 accuracy를 비교하여 최종적으로 하나를 선택할 예정이다.</p>
<p>KoGTP-2 모델이 반환할 진료과목수는 가정의학과, 내과, 외과, 신경과, 한의학과 등 16개로, 약국 정보는 여기 포함되어 있지 않은데, 우리 서비스 사용자의 input value중 하나인 필요로하는 진료 수준(Necessary Medical Measures)에 따라 인공지능 모델을 돌려 분과명을 추출하지 않고 DB에서 직접 반환하도록 하였다. 현재 수집한 DB는 서울시 공공데이터 및 보건소 홈페이지로부터 크롤링한 것으로(중앙응급의료센터(<a href="https://www.e-gen.or.kr/intro/intro.do">https://www.e-gen.or.kr/intro/intro.do</a>) 및 각 자치구 보건소), 서울시 내 약 40000개의 병원 정보와 400여개의 외국어 지원 가능 병원 정보, 외국어 사용이 가능한 수백여개의 약국 정보 등이다(JSON 변환 후 Bulk Create, 총 39065개 + a). </p>
<p> KoGTP2 모델은 구현되어 있지만 Fine-tuning용 데이터를 수집하는 단계에 아직 머물러 있는데, 과도하게 crawling을 요청하면 서버로부터 IP Ban을 당하는 것 같다. 관련 이슈에 대해 의논한 결과, 현재 팀 내에서 사용 가능한 해결책은 크게 두 가지로, Thor를 사용해 VPN효과를 얻는 것, 다른 하나는 도커 인스턴스를 여러 개 만들어 서버에 crawling 요청을 하는 것이었다. Thor를 사용해 크롤링하는 것에는 성공했지만 속도가 매우 느린 것이 단점이어서, 도커를 사용하는 방법을 시도하는 중에 있다.</p>
<p> 백엔드 서버에서 데이터 플로우를 도식화 해보면 다음과 같다. 
 <img src="https://velog.velcdn.com/images/vlv_3/post/9e0f3a0b-a8bc-471f-8df9-1a0ca406c905/image.png" alt=""></p>
<p> WAS와 WS는 상기한 대로 구현되고 있지만, 문제는 서버 부하를 줄이기 위해 존재하는 Cache Server의 구현에 있다. 외국어 Query를 한국어로 번역한 뒤, Cache server의 Q&amp;A 테이블에 저장된 Query를 전문검색(Full-text query)하고자 한다. 입력받은 query와 cache server에 저장된 query의 유사도(Similarity)를 판단하여 정확한 Answer를 반환하기 위해서이다. Answer 정보는 캐시 서버에 저장된, 다른 사용자의 검색 당시 현재 위치를 제외한 나머지 병원정보들을 일컫는다. 
 <img src="https://velog.velcdn.com/images/vlv_3/post/b1e1cd0f-9bdc-4169-b4df-c1255087c625/image.png" alt="">
두 문장간 유사도를 측정하기 위해 크게 세 가지의 방법을 의논하고 있다. 첫째, 자카드 유사도 기반 검색 알고리즘을 도입할 수 있다. <img src="https://velog.velcdn.com/images/vlv_3/post/b3ef0a95-3192-40e1-b045-7c779ae0ccaa/image.png" alt="">
아주 단순하지만 그만큼 가벼운 방법론으로, 두 문장이 공통적으로 사용하는 단어의 갯수를 두 문장에 사용된 총 단어의 갯수로 나눈 값을 두 문장 사이의 유사도로 삼는 것이다. 그러나 우리 서비스는 두 한국어로 번역된 문장 사이의 유사도를 측정하는데, 한국어 형태소는 의미가 유사하지만 다른 표현 방식들이 존재하고(가령 먹고, 먹으니, 먹되, 먹는 등은 기본형은 동일하지만 표현방식이 조금씩 다르다), 같은 동음이의어가 존재할 수 있다(가령 &quot;사과&quot;라는 단어는 맥락에 따라 apple일수도, appology일 수도 있다). 따라서 자카드 유사도가 두 query의 의미론적 유사성을 나타내는 충실한 지표라는 보장이 없다. 두 번째 방법은 tri 자료구조 형식으로 query 문장 전체를 저장하고, 자료구조 중간부터 탐색 가능한 탐색 알고리즘을 개발하여 search하는 것이다. 이 아이디어는 즉흥적인 것으로 효율성이 떨어져 폐기하였다. 세 번째 방법은 KeyBERT model을 사용해 문장의 중심 키워드를 몇 개 추출한 뒤, 이 keywords로 ElasticSearch 기반 DB를 search하는 것이다. 자카드 유사도 기반 알고리즘이 가진 신뢰성 문제를 해결하는 데 도움이 되지만, 기존의 KoGTP2 기반 분류 모델의 무거움을 해소하고자 cache서버를 도입하는 입장에서, 다른 인공지능 모델을 추가하는 것이 과연 좋은 선택일지 의문이라는 점이다. 따라서 모델 또는 cache서버 경량화를 위한 별도의 구상이 필요한 시점이다. </p>
<p>또 다른 당면과제는 서버간 부하 분산 issue이다. 가령 WAS에 너무 많은 부담이 몰리게 되면, WS는 순서에 따른 데이터 처리가 어려워지면서 전반적인 서비스 속도의 저하로 이어질 수 있다. Nodejs는 non-blocking single thread system이므로, 비동기 처리의 강점을 살리면서 부하를 분산하는 로드밸런싱 기법이 필요할 수 있다. Docker / k8s는 사용가능한 선택지 중 하나로, AWS Kinesis와 병렬 사용 가능한지 여부를 탐색하고 있다. 멘토님께서는 AWS ECS fargate를 추천해주셨는데, 우리 서비스에 어떻게 접목할 수 있을 지 의논중에 있다. 또한 현재는 우리 서비스가 Javascript &amp; express 서버로 구현되고 있지만, 차후 MSA 형태로 리팩토링할 때 Nest.js의 모듈 기반 아키텍쳐 구현 방식이 도움이 될 것으로 보고 있다. 따라서 Nest.js 스터디를 4~6회 정도로 운영할 생각이다. </p>
<p>마지막으로 병원 Data의 validation 문제가 남아있다. 어떻게 병원 DB의 최신성을 유지할 수 있을까? 어떤 병원은 외국어 지원 가능 여부가 달라질 수도 있고, 폐업하거나 이전할 수도 있다. 지금은 Kakao API를 사용해 병원의 폐업 여부를 탐색하고 있지만, 우회로로써 외부 API에 의존하지 않는 더 나은 방법을 의논중이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트-백엔드 협업 첫경험 후기]]></title>
            <link>https://velog.io/@vlv_3/%ED%94%84%EB%A1%A0%ED%8A%B8-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%98%91%EC%97%85-%EC%B2%AB%EA%B2%BD%ED%97%98-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@vlv_3/%ED%94%84%EB%A1%A0%ED%8A%B8-%EB%B0%B1%EC%97%94%EB%93%9C-%ED%98%91%EC%97%85-%EC%B2%AB%EA%B2%BD%ED%97%98-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 30 Oct 2022 09:47:16 GMT</pubDate>
            <description><![CDATA[<p>항해99 프로그램은 프론트와 백엔드 개발지망자를 나누어 학습시키고, 이들 사이에 협업하는 커리큘럼을 포함하고 있다. 지난 주, 수 주간 각자 사이드에 훈련받은 뒤, 그룹별로 사이드간 팀을 이루게 되었다. 우리 팀은 nodejs로, 상대 프론트엔드 사이드는 React를 공부하신 팀이었다. 기존의 서버사이드 내 커뮤니케이션과 달리, 첫 협업은 매우 신선했다. </p>
<p>대망의 첫 미팅날, 1주일 내로 개발할 수 있는 스코프의 작은 프로젝트의 기획을 시작하게 되었다. API 명세서를 짜고 변수 이름이나 모듈을 통일한 다음에, 프론트사이드에서 제안한 협업 주제는 놀랍게도 홈페이지의 대표색상을 정하는게 좋겠다는 것이었다. 아직 협업 프로젝트에 디자이너가 붙지 않기 때문에, 프론트단에서 이것을 결정해야 할 필요가 있었는데, 이것을 서버사이드도 같이 정했으면 좋겠다는 말씀이셨다. </p>
<p>와우.. 나는 여태껏 ORM 다루고 Validation을 정의하다가 색상을 정하는 문제를 공유하게 되었다. 물론 실제로 이런 문제를 서버사이드에서 고민할 일은 앞으로 거의 없지 않을까 싶다(풀스택 개발자가 되는 날이 온다면 모르겠지만). 그럼에도 나는 이 질문이 백엔드 개발자에게 작게나마 가치있을 수 있다고 생각했는데, 그들이 사용하는 언어나 맥락을 이해하는 것이 이들과의 커뮤니케이션을 더 낫게 만들어 줄 수 있다고 믿기 때문일 것 같다. 설사 그것이 양질의 서버 기능을 구성하는 것과 관련이 없더라도, 이들이 고민하는 것을 같이 고민해보는 것은 나를 아주 즐겁게 만들었다. </p>
<p>우리가 만들 프로젝트는 환경보호를 위한 동참을 유도하기 위해 (1) 유저가 일종의 퀘스트(환경보호 목적을 위한 챌린지)를 만들고, (2) 퀘스트에 동감하는 한정된 수의 유저들이 이 퀘스트 포스팅에 참여하여, (3) 포스팅 후 공유하도록 하는 것이었다. 지속적인 행동 강화를 위해 퀘스트 보상으로 챌린지를 처음 시작한 유저가 일종의 Score를 거는 시스템, 강화 체계(단발성 보상을 줄지, 지속적으로 수행했을 때 가중치를 줄 지)를 어떻게 챌린지에 따라 어떻게 정할지 등을 첫 날 정의하였다. 너무 큰 스코프는 결국 죽도 밥도 안된다는걸 알고 있으면서도, 기획 자체가 주는 재미는 서로 다른 view를 가지고 있는 두 팀이 만나면서 폭발했던 것 같다 :). 1주일 동안 모두 구현하는 것은 결국 실패했고, HTTPS로 배포된 프론트와 HTTP로 구현된 Express 서버간 쿠키를 주고받지 못하는 문제(CORS 모듈을 사용할 때 withCredential option이 제 기능을 수행하지 못하는 것 같다)가 발생했고, 결국 JWT 방식으로 구현된 로그인/로그아웃 서비스를 어떻게든 살리기 위해 서버를 HTTPS로 바꾸어 배포하려다 타임아웃이 되버렸다. 원래 목표로 잡았던 기능을 제공하기 위해 만들어두었던 정상적인 API들이, 배포 단계에서 실제로 사용도 못해보고 사장되버렸다. 이미 구현했던 것들이 아쉬운건 아니었다. 알고 있으면 언제든 또 만들 수 있다. 그러나 만들고 싶었지만 만들지 못했던 것, 반드시 필요하지만 필요한지 알지도 못했던 것들(배포 단계에서 생길 수 있는 문제들 등)에 대해 팀원들 간 고민하고 공유하지 못했던 것이 날 짜증나게 만들었다. 무능한 나.. 흑흑 ㅜㅜ 다만 이제 할일은 이번에 필요성을 체험한 것들과 실패를 잘 기록하고 다음엔 같은 실수를 안하도록 하는 것이다. </p>
<p>기록 첫 번째, 기획을 하루만에 끝내고 남은 6일간 수정하는 것 보다 3일동안 기획하고 끝내는게 낫다. 우리 조는 첫 날, 수 시간만에 기획 단계를 끝내고 바로 코딩에 들어갔지만, 처음 API 명세서를 작성할 때와 프로젝트가 끝나고 나서 API 명세서는 내용물이 상당히 차이가 났다. 중간중간 변수 이름이 바뀌거나, 서버에서 response로 돌려주는 값이 달라지거나 추가되기도 했다. 이러다보니 프론트와 서버 사이에 자잘한 질문-응답이 여러 번 추가되었고, 담당 개발자가 자리에 없으면 진행이 딜레이되는 경험을 하기도 했다. 기획 단계에서 요구되는 것에, 조금 더 시간이 들더라도 완벽에 가깝게 만들었다면, 불필요한 단계였을 것이다. </p>
<p>두 번째, 기획은 크게 잡더라도, 개발 단계에서의 스코프는 아주 작아야 한다. 기능 가능한 최소 단위만 구현한 뒤, 배포하고 서버-클라이언트 사이의 연결이 의도한 대로 이루어지는지 파악할 필요가 있다. 최소 단위의 기능이 제대로 구현된다는 확신이 있다면, 그 뒤에 하나하나 새로운 기능을 쌓아가는 것이 새로운 에러를 찾기 용이하다. 지난 주에는 넉넉히 이틀만에 필요한 API 대부분을 구현했는데, 상기했듯 제대로 쓰지도 못하고 사장되었다. 이런 실수는 다시 없어야한다.</p>
<p>세 번째, 잘하는 한 사람만 모두 떠안는 건 바보같은 일이다. 만약 내가 팀에서 약간이나마 더 나은 점이 있다면, 즉시 그리고 정확한 내용을 공유해야 한다. 팀이 성장하면 업무가 분담될 수 있고, 믿을 수 있는 팀원들이 받쳐주면 도전적인 사람은 비로소 도전적인 작업에 몰두할 시간이 생긴다. 팀을 키우는 것은 그 소속원 개인을 키우는 것과 크게 다르지 않을 것이다. 문제는 어떻게 팀을 의도한 수준까지 키울 수 있는가 일것이다. 여러 알지 못하는 방식이 존재하겠지만, 우선 팀원 개인과 친해질 필요가 있다고 믿는다. 팀원이 무엇이 관심이 있는지, 무엇을 싫어하는지, 무엇은 잘하고 못하는지 알면 어떤 역할을 분배할 수 있을지도 알 수 있을 것이다. 최근 그 사람을 사로잡고 있는 일이 있다면(여친하고 헤어진 나처럼.. 흑흑), 비록 사소하더라도 그 일에 집중할 수 있도록 배려해줘야 한다. 그 사람만 할 수 있는 일이라면 내가 배워야 한다. 이런 상호작용 속에서 공통의 목표를 공유할 수 있다면 성공일 것이다.</p>
<p>네 번째, 뭐든 즐겁게 하자. 즐거우면 길게갈 수 있고, 길게 가려면 여유도 필요할 것이다. 1일 13시간, 1주일 내내 하나만 붙잡고 있으면 아무리 그것이 재밌어도 지칠 수 있다. 아무리 바빠도 내 삶, 여유, 내 주변 사람들에게 관심을 끊어선 안될 것이다. 나중엔 그게 너무너무 아플 수 있다... ㅜ</p>
<hr>
<p>오늘은 잠실 석천호수에서 산책도 하고, 겸사겸사 근처 카페에서 Next.js를 공부하고 있다. 오늘도 거의 끝나간다. 부디 훗날 돌이켜 봤을 때 오늘을 후회하지 않기를 빌며... </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL과 ORM]]></title>
            <link>https://velog.io/@vlv_3/SQL%EA%B3%BC-ORM</link>
            <guid>https://velog.io/@vlv_3/SQL%EA%B3%BC-ORM</guid>
            <pubDate>Sun, 16 Oct 2022 13:39:18 GMT</pubDate>
            <description><![CDATA[<p>서버와 클라이언트는 필연적으로 데이터를 주고 받는다. 어떤 데이터는 서버에 저장해두고, 클라이언트가 요청할 때 마다 그것을 꺼내주거나, 수정하기도 하고, 새로운 데이터 공간에 창출된 데이터를 저장하거나, 지우기도 한다. 모든 정보가 저장될 필요는 없다. 그러나 어떤 정보는 반드시 저장되어야 한다. 가령 유저의 닉네임과 패스워드는 클라이언트로 하여금 서버의 감춰진 특정 정보에 접근하게 하거나, 또는 그렇지 못하게 만들기 위해 필요하다. 또 클라이언트 요청을 처리하기 위해 서버의 데이터를 활용하기도 한다. 개발자는 반드시 저장이 필요한 정보를 웹 서버(Web server)에 저장하고, 웹 서버는 웹 어플리케이션 서버(Web Application Server)로 필요한 정보를 보내 연산한 뒤 결과물을 송신한다. 서버의 리소스는 한정적이므로, 서버를 둘 이상으로 나누어 다른 역할을 부여하기도 한다. 같은 이유로 데이터를 어떻게 저장할 것인지를 정의하는 것은 리소스를 효율적으로 사용하기 위해 중요한 요소이다. 같거나, 기능적으로 유사한 정보를 중복 저장하는 경우는 없어야하고, 필요하지 않은 정보는 제거해야한다. 데이터를 검색하는 방식에 있어서도 마찬가지일 것이다. 가령 모든 데이터를 처음부터 하나씩 비교하는 것도 가능하지만, 데이터 그룹을 나누어 저장한다면 그루핑을 하기 위한 기준이 곧 데이터 검색을 위한 주소로 기능할 수도 있을 것이다. </p>
<p>SQL(Structured Query Language)이란 관계형 데이터베이스 시스템(RDBS, Relational DataBase System)을 처리하기 위한 언어이다. 데이터베이스가 서버가 보유한 데이터의 집합이라면, 관계형 데이터베이스는 데이터를 어떤 양식으로 묶을지에 대하여 가리키고 있다. 관계형 데이터베이스의 특징 중 하나는 데이터가 테이블에 저장된다는 것이다. 테이블은 특정한 정보들을 양식에 맞게 받아 저장한다. 예를 들어 User 테이블이 있을 수 있겠다. 개발자는 목적에 따라 몇 가지 데이터를 묶어 User 테이블에 저장할 것이다. 단, 테이블에 저장되는 최소 단위의 데이터 그룹은 타입이 반드시 고정된다. 유저의 이름, 이메일, 패스워드, 주소, 의도에 따라 어떤 타입이던 가능할 것이다. 그러나 테이블에 미리 지정한 데이터 타입이 아닌 다른 데이터, 가령 Goods라는 테이블에 들어가야 할 상품 정보 데이터 타입을 추가로 User 테이블에 저장될 수 없다(물론 기존의 유저 주소 대신 상품 정보를 저장할 수 있겠지만, 무의미할 것이다. 엄밀히 규제되는 것은 데이터의 타입이다). User 테이블이 가지고 있는 고정된 데이터 타입 집합이 정의된 것을 스키마(Schema)라고 부른다. </p>
<p>한 테이블에 들어갈 수 있는 데이터의 타입을 고정하는 것이 무슨 의미가 있을까? 첫 번째는 중복 데이터 저장을 억제할 수 있다는 장점이 있다. User 테이블에 저장될 정보는 다른 스키마 형태를 가지는 테이블에 저장될 수 없다(그럴 필요가 없다). 유저 정보는 오직 User 테이블에만 저장될 것이다. 유저 정보에 접근이 필요하다면, 모든 데이터를 하나하나 살펴볼 필요가 없다. 둘째, 은밀성이 필요한 정보에 대한 접근성을 정의할 수 있다. 이것은 데이터의 타입을 고정하기 때문이기보다, 테이블을 사용하여 스키마 양식을 구분함으로써 얻을 수 있는 이익일 것이다. 셋째, 개발자가 데이터 뭉치를 한눈에 보기 용이하다는 장점이 있다. 넷째, 무결성 보장을 위해 데이터 타입을 고정할 필요가 있다. 예를 들어 User 테이블에 저장되는 최소 단위의 데이터 뭉치는 유저 이름, 이메일, 패스워드 등 테이블이 요구하는 모든 데이터가 포함되어야 한다. 그들 중 하나가 빠진다면, 최소 단위의 데이터 뭉치(이하 데이터 뭉치)는 기존의 User 테이블에 저장되어 있는 데이터 뭉치 하나와 동일한 의미를 가지고 있지 못하다. 가령 유저 이름이 빠진 데이터 뭉치는 온전한 유저 데이터 뭉치와 달리 현실세계의 누구를 가리키는가에 대하여 정확한 답을 내놓지 못할 것이다. </p>
<p>하나의 테이블이 저장하고 있는 데이터 뭉치 내부의 정보 간 상호 연결을 통해 &quot;나는 누구인가?&quot;와 같은 질문에 답할 수 있다면, 데이터 뭉치 외부, 크게는 테이블 간 데이터 뭉치의 연결을 통해 &quot;서비스 이용자의 희망에 따라 어떤 서비스를 제공해야 할 것인가?&quot; 처럼 보다 총체적인 질문에 답할 수 있을 것이다. SQL은 테이블 간 정보의 연결(Join)의 구현을 시스템적으로 지원하고 있다. 가령 User 테이블의 한 유저와, Goods 테이블의 그 유저의 주문 상품 목록을 연결하면 복합적인 상호관계를 한 눈에 파악할 수 있을 것이다. 각각의 테이블에 관련 정보를 저장하고 이들의 관계만을 연결하는 것은 데이터 중복 저장을 억제하는 효과도 있다. </p>
<p>상기한 바와 같이, 관계형 데이터베이스의 가장 큰 특징 중 하나는 테이블에 저장되는 데이터 뭉치 양식을 고정하는 것, 그리고 테이블과의 연결성이라고 할 수 있다. 두 특징은 RDBS로 구현된 서버 DB를 운용할 때 상기한 여러 강점을 사용자에게 제공해주었지만, 동시에 그 경직성은 몇 가지 난점도 가지고 있다. 데이터 스키마 양식은 개발자가 정의한 시점부터 고정되어 있기 때문에, 나중에 새로운 양식을 추가하거나 제거해야 하는 경우 문제가 될 수 있다. 또 데이터간 복잡한 연결관계를 표현해야 하는 경우, 개발 난이도가 높아질 수 있을 것이다.
대체로 수직적 확장(CPU 기능 향상 등)만이 가능하고, 더 많은 서버를 추가하여 데이터를 분산시키는 확장 방식은 SQL 방식과 맞지 않을 가능성이 있다(같은 양식과 이름을 가진 두 개 이상의의 스키마가 존재하게 될 것이고, 연결관계는 더 복잡해지고, 어디서 데이터를 찾아야할 지 확정할 수 없는 등 SQL의 장점을 충분히 활용하기 어려워질 것이다). 그러나 수직적 확장은 단순히 서버 몇 대를 추가하는 것보다 더 많은 리소스를 소비한다. 성능이 좋을수록, 비슷한 수준으로 성능을 향상시키기 위해 더 큰 비용이 소비될 수 있다. NoSQL(Not Only SQL) 방식의 DB 구현은 이러한 SQL 방식의 한계를 극복하고자 비교적 최근에 제안된 구현 방식이다. NoSQL이 무엇인지, SQL과 대척점에 있는 것으로 말하기는 어려울 것이다. NoSQL은 이름 그대로 SQL의 기능을 일부 포괄하여 기능할 수 있기 때문일 것이다. 그러나 일반적으로 NoSQL의 특징이라고 하면, 스키마가 존재하지 않을 수 있다는 점(Schema-less), 그리고 연결관계(join)의 정의가 필요하지 않을 수 있다는 점일 것이다. 역설적이게도 SQL 방식의 가장 큰 장점이자 특징을 버림으로서, NoSQL은 사후에 데이터 양식의 조정이 용이하고 수직, 수평적 확장 등이 가능하다는 장점을 가지게 되었다. 스키마와 연결관계가 없는 대표적인 NoSQL중 하나로 MongoDB를 꼽을 수 있을 것이다. MongoDB는 NoSQL로서 기능을 유지하면서 스키마(Schema), 연결(Populate)과 같은 SQL 기능을 더해주는 ODM(Objective Document Mapping)인 Mongoose와 자주 사용된다. </p>
<p>SQL 역시 ORM(Objective Relation mapping)을 통하여 접근하기도 한다. NoSQL을 기반으로 데이터 무결성 등을 보장하기 위하여 사용되는 ODM과 ORM은 기대되는 역할이 조금 다른데, ORM은 객체 형식을 통해 여러개의 테이블로 구성된 관계형 데이터베이스를 다루는 데 도움을 준다. SQL은 SQL을 위한 별도의 쿼리문 집합을 통해 테이블을 구성하는데, 이것은 서버를 구현하기 위해 사용되는 자바, 자바스크립트, 파이썬 등과 문장 형식에 있어 차이가 존재한다. ORM은 이들 사이의 간극을 메우는 데 초점을 두고 있다. 비록 ORM으로 완전히 SQL을 대체하지 못하더라도, 객체를 통해 테이블과 연결관계를 구현하는 것은 보다 직관적이며, 가독성이 좋기에, 개발자의 생산성에 긍정적인 영향을 미칠 수 있을 것으로 기대할 수 있을 것이다.  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IIFE와 require 함수]]></title>
            <link>https://velog.io/@vlv_3/IIFE%EC%99%80-require-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@vlv_3/IIFE%EC%99%80-require-%ED%95%A8%EC%88%98</guid>
            <pubDate>Thu, 13 Oct 2022 09:46:30 GMT</pubDate>
            <description><![CDATA[<p>require 함수는 하나의 자바스크립트 확장자 파일 내부에서 동작할 외부 모듈을 불러오기 위해 사용됩니다. 외부로부터 여러개의 임의의 모듈을 불러오는 것은, 편리하지만 그럼에도, 서로 다른 모듈 작성자가 우연히 같은 이름의 변수를 각각 지정함으로써 일어날 수 있는 오류의 가능성을 내포할 수 있습니다.</p>
<p>자바스크립트는 ES5부터 require 함수를 지원하고 있는데요. 외부 모듈을 하나의 파일에 성공적으로 불러오기 위해서 하나의 모듈이 다른 모듈을 침범해선 안된다는 조건은 당시 개발자들에게도 화두였던 것 같습니다. 작은 프로그램을 개별적인 단위로 분리하는 것을 모듈화(Modularization)라 하고, require 함수는 모듈화를 위해 모듈 자신만의 독립적인 변수 처리를 보장하는 스코프 지정을 가능하게 합니다. 이 때 (여러 방법이 있겠지만) 모듈화를 위해 require 함수에서 사용한 방식을 즉시 실행 함수 표현(이하 IIFE, Immediately Invoked Function Expression)이라고 부릅니다. IIFE란, 정의한 즉시 실행되는 함수 표현식을 말합니다. IIFE를 사용함으로써 함수의 단 한번의 반환이 보장받을 수 있고, (함수 표현식으로 사용되는 경우) 호이스팅되지 않기에 인터프리터가 함수를 읽어들이는 단계에서 사용됨으로써 내부 변수가 외부에서 참조될 가능성을 줄일 수 있습니다. 또 일회성 함수이므로 전역 네임스페이스에서의 충돌 가능성을 방지할 수도 있겠습니다. 어떻게 그것이 가능한지, require 함수의 구조와 함께 이야기해보겠습니다.</p>
<p>require 함수의 내부를 간략히 표현하면 다음과 같습니다.</p>
<pre><code class="language-javascript">var require = (function(src){
var fileAsStr = readFile(src)                //src 파일을 읽어 와 fileAsStr에 저장합니다. 
    var module.exports = {}                  //빈 해쉬를 선언합니다.
    eval(fileAsStr)                          //fileAsStr을 실행하고 해쉬를 업데이트 합니다.
    return module.exports                    //해쉬를 반환합니다. 
})();</code></pre>
<p>변수 require에 반환할 결과물을 만들어내는 익명함수는 소괄호로 둘러 쌓여있고, ()와 같은 함수의 즉시 실행 옵션이 소괄호 끝에 붙어있다는 점이 특이합니다. 함수 내부에 변수를 정의하면 이 변수는 외부에서 참조할 수 없으므로 독립성을 가지게 되는데, 이것은 require가 함수이기 때문에 가지는 특징입니다. 또 필요한 경우, 함수 선언식(statement)이 아닌 표현식(expression)으로 사용하여, 하나의 값으로서 특정 변수에 저장 및 재사용이 가능합니다. 마지막으로 즉시 실행되고 마침으로써 의도하지 않은 함수 재참조를 방지할 수 있다는 장점이 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Restful API란]]></title>
            <link>https://velog.io/@vlv_3/Restful-API%EB%9E%80</link>
            <guid>https://velog.io/@vlv_3/Restful-API%EB%9E%80</guid>
            <pubDate>Sun, 09 Oct 2022 06:10:49 GMT</pubDate>
            <description><![CDATA[<p>RESTful API란 일반적으로 REST(Representational State Transfer) API를 제공하는 웹 서비스를 일컫는다. RESTful API가 무엇인지 답하기 위해서는, REST의 정의가 핵심이라고 할 수 있다. REST를 가리키는 한국어 표준 단어가 있는지 모르겠지만, 굳이 표현하자면 &quot;표현적 상태 전송&quot; 정도로 볼 수 있을 것 같다. REST는 무엇을 표현하고, 상태는 무엇의 상태를 말하는가?</p>
<p>서버와 클라이언트 사이의 명료한 구분은 블록체인 기술이 등장한 현대시대에 다소 불분명해진 부분이 있지만, 서버는 자원(Resource)을 저장하고, 클라이언트는 요청한다는 구분은 여전히 주효하다고 할 수 있다. 서버가 가지고 있는 자원은 영구하지 않다. 비록 자원을 가리키는 식별자의 이름은 동일하더라도, 그 식별자가 가리키는 변수 내부의 데이터는 의미를 유지하면서도 변화할 수 있다. 가령 올해의 내 카드 사용 내역을 &quot;나의 카드 사용 내역&quot;이라는 변수 내부에 저장하고 있다면, 한 해가 끝나갈수록 데이터는 점차 많아질 수 있다. 때로 신용 거래를 취소해야 할 경우도 있는데, 이때도 데이터는 변화한다. REST는 서버가 저장하고 있는 변수 내부 데이터의 현재 상태(State)를 송, 수신할 것을 전제한다. 데이터의 현재 상태는 데이터 집합의 의미를 대표하는 특정한 식별자로 표현(Representation)된다. 서버와 클라이언트 사이의 데이터 송수신을 말할 때, 이렇듯 표현형에 따른 정보 상태의 전달이라는 관점은 RESTful한 웹 서비스와 그렇자 않은 경우를 구분하는 특별한 기준점이라고 보기 어렵다. RESTful한 웹사이트인지 구분하기 위한 기준은 그 웹서비스가 REST라는 정의에 충족하는지가 아니라, 그것이 포함하고 있는 문제의식을 개발자가 공유하고 있는지와 더 가깝다고 볼 수 있다.</p>
<p>RESTful한 구현이 필요한 이유는 그것이 보다 직관적이고, 또 직관적인 표현(Represenatation)으로 통일함이 필요한 경우가 있기 때문이다. 하나의 서버는 수많은 클라이언트 각각과 상태 전송을 전제한다. 어떤 클라이언트는 나의 웹 서비스를 컴퓨터로 사용할 수 있지만, 다른 클라이언트는 모바일 환경에서 사용할 수도 있고, 둘을 동시에 사용할 수도 있다. 같은 서비스를 멀티 플랫폼으로 제공하는 것을 전제하는 상황에서, 자원의 표현형이나 자원을 다루는 방식의 표현 등 개발 인터페이스를 통일하는 것이 합리적이다. REST는 HTTP Protocol을 기반하고 있으므로, HTTP Protocol을 따르는 모든 플랫폼에서 RESTful한 구현이 가능하다. HTTP Protocol을 따른다는 것은 이외에도 다양한 장점을 RESTful하게 구현했을 때 개발자 등에게 제공하는데, 가령 무상태성(Stateless)을 통해 서버 부하를 감축시킬 수 있다던지, 캐시 데이터 처리가 가능하다던지 하는 것이다. 하지만 이것은 HTTP Protocol을 사용한 웹 서비스 구현에 따른 장점이지, 오롯이 RESTful한 구현에 따른 장점이라고 볼 수 있다. RESTful한 구현은 HTTP Protocol 사용과 더불어, 앞서 말한 통일성에 그 장점이 있다. API를 구현할 때 RESTful한 구현 방식으로 통일하게되면, 하나의 구현 방식을 공유하게 되므로 확장성, 재사용성이 높아지므로 생산성을 높이거나 유지보수를 하기에 보다 용이해진다. 또 그 표현형이 직관적이므로, 다른 언어로 같은 웹 서비스를 구현할 필요가 있을 때에도 편리한 측면이 있다.</p>
<p>그렇다면 어떻게 해야 RESTful한 구현이 가능할까? 어떤 명확한 표준이 존재해서 RESTful한 구현과 그렇지 않은 것을 구분할 수 있는 것은 아니지만, 일반적으로 통용되는 몇 가지 규칙이 존재한다. 
서버의 모든 자원은 다른 자원과 구분되는 고유한 ID를 가진다. RESTful한 구현은 클라이언트가 HTTP URI라는 고유한 정보를 가지고 서버 자원에 접근할 것을 전제한다. URI(즉 고유한 식별자)는 담고 있는 Resource를 명확하게 표현해야 한다. 예를 들어 자원 그 자체는 동사보다 명사형 단수 표현이 좋고, 대문자보다 소문자 사용이 지향된다. 그러나 여러 자원을 담고 있는 컬렉션이라면, 복수형 명사 표현이 보다 직관적이다. 
자원의 상태는 클라이언트 요청에 따라 달라질 수 있다. 단순히 서버의 자원을 내려받을 것을 요청할 수 있고(GET), 새로운 데이터를 추가하거나(POST), 변경하거나(PUT), 제거할 수 있을 것이다(DELETE). 식별자인 HTTP URI와 클라이언트의 요청은 명확히 구분되어야 한다. URI에 요청과 같은 동사형 표현이 들어가선 RESTful하다고 보기 어렵다. 
HTTP method에 따른 클라이언트의 행위(Verb)는 서버로 하여금 적당한 응답을 하게 만든다(Representation of Resources). 이 응답은 JSON일수도 있고, XML일수도 있으며, 그 외의 다른 형태로 표현될 수 있다. 
그 외에도 URI는 슬래쉬(/)문자로 끝나지 않아야 한다던지, 밑줄(_)을 URI에 사용해선 안된다던지, 파일 확장자는 URI에 포함해선 안되는 등 몇 가지 규칙이 더 있다. </p>
<p>RESTful API의 예시로 다음과 같은 예가 있다. </p>
<blockquote>
<p>출처: <a href="https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html">https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html</a>
<img src="https://velog.velcdn.com/images/vlv_3/post/056dbc56-a130-44ec-a6e4-2c4e74ac8fe9/image.png" alt=""></p>
</blockquote>
<hr>
<p>추가. ) 이번 주차에선 Package.json에 대하여 새로 알게되었다. Package.json은 현재 내 서비스에 설치되어 있는 Package와 버전 정보가 담겨 있다. node_modules없이 배포하는 경우 Node Package Manager를 사용해 일괄적으로 의존적인 packages를 install할 수 있지만, Package.json에는 package의 특정 버전이 아닌 버전 범위(Version range)가 저장되므로, package의 특정 버전까지 일치할 필요가 있다면 Package-lock.json을 사용해 install할 필요가 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서버는 어떻게 IP가 같은 두 브라우저를 구분할까? - OSI 7 Layer를 중심으로]]></title>
            <link>https://velog.io/@vlv_3/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-IP%EA%B0%80-%EA%B0%99%EC%9D%80-%EB%91%90-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EB%A5%BC-%EA%B5%AC%EB%B6%84%ED%95%A0%EA%B9%8C-TCPIP-%EC%86%A1-%EC%88%98%EC%8B%A0%EC%9D%84-%EC%A4%91%EC%8B%AC%EC%9C%BC%EB%A1%9C</link>
            <guid>https://velog.io/@vlv_3/%EC%84%9C%EB%B2%84%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-IP%EA%B0%80-%EA%B0%99%EC%9D%80-%EB%91%90-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%EB%A5%BC-%EA%B5%AC%EB%B6%84%ED%95%A0%EA%B9%8C-TCPIP-%EC%86%A1-%EC%88%98%EC%8B%A0%EC%9D%84-%EC%A4%91%EC%8B%AC%EC%9C%BC%EB%A1%9C</guid>
            <pubDate>Thu, 06 Oct 2022 10:46:06 GMT</pubDate>
            <description><![CDATA[<p>며칠 전, 저희 팀원분들과 이야기를 나누던 중 문득 궁금한게 하나 생겼습니다. 당시 공유했던 의문을 의식의 흐름 기법으로 표현하자면(ㅋㅋ) 다음과 같습니다. 
 &quot;서버는 내가 보낸 요청 메시지에 적힌 아이피 주소를 보고 내 아이피 주소로 내가 요청한 정보를 보내주는구나. 좋아. 근데 사파리 브라우저로 서버와 통신하나 크롬 브라우저로 서버랑 통신하나 내 아이피는 동일할 것 같은데, <strong>어떻게 사파리 브라우저로 보낸 요청을 서버는 헷갈리지 않고 사파리 브라우저를 향해 response할 수 있을까?</strong>&quot;</p>
<hr>
<h1 id="클라이언트와-서버-사이의-네트워크-통신">클라이언트와 서버 사이의 네트워크 통신</h1>
<p>결론부터 말하면 TCP secment 송, 수신이 그것을 가능하게 합니다. 서버에도 포트번호가 있듯이, 클라이언트도 사용하는 애플리케이션마다 포트번호가 있기 때문이었어요!
이 과정을 보다 자세히 이해하기 위해 클라이언트와 서버 사이의 데이터 송수신 과정 전반을 이미지를 통해 먼저 살펴보겠습니다(TCP school 출처). 
<img src="https://velog.velcdn.com/images/vlv_3/post/98b09d44-cac1-4cc1-bfe6-0d921a432521/image.png" alt=""></p>
<p>우선 사용자가 웹 브라우저를 통해 원하는 도메인을 검색창에 직접 입력했다고 가정하겠습니다<strong>(1)</strong>. 웹 브라우저는 제가 입력한 도메인 주소를 HTTP protocol에 맞게 포장하고<strong>(2)</strong>, 통신사 DNS 서버로 전송합니다<strong>(3)</strong>. 통신사 DNS 서버는 dot(.)으로 구분된 도메인 주소 일부를 가지고 최종적으로 제가 입력한 도메인 주소와 매칭되는 IP를 찾기 위해 도메인 주소 일부와 경로 정보가 저장되어있는 다른 여러 DNS 서버(도메인 이름이 저장되어 있어 name server라고도 합니다)와 송수신 과정을 거칩니다. 검색한 도메인 주소와 매칭되는 IP를 찾는 것에 성공했다면<strong>(4)</strong>, 브라우저는 IP 주소를 건내받아 HTTP 프로토콜을 사용해 HTTP 요청 메시지를 생성하게 됩니다<strong>(5)</strong>. 이 HTTP 요청 메시지는 TCP 프로토콜을 사용해 여러 경로(정확히는 최적 거리에 있는 Router 사이의 송,수신)을 거쳐 해당 IP 주소를 가진 서버로 전송됩니다. 도착한 HTTP 요청 메시지는 TCP protocol<strong>(6)</strong>, HTTP protocol을 거쳐 웹페이지 URL 정보로 변환됩니다<strong>(7)</strong>. 서버는 요청한 정보에 맞춰 필요한 처리가 있다면 그것을 처리한 뒤<strong>(8)</strong>, 그 결과물을 HTTP 프로토콜을 사용해 포장하는데, 이것이 HTTP 응답 메시지가 됩니다<strong>(9)</strong>. 이후 TCP 프로토콜을 사용한 뒤, 어쩌면 거쳐왔을지도 모르는 경로(Router)를 거쳐 내 IP 주소로 이동합니다<strong>(10)</strong>. HTTP 응답 메시지는 다시 TCP-&gt;HTTP 프로토콜을 거쳐 웹페이지 데이터로 변환됩니다<strong>(11)</strong>. 이제 웹 브라우저는 서버로부터 받은 데이터를 바탕으로 요청한 콘텐츠를 표시하는 렌더링 엔진과, 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어하는 브라우저 엔진을 거쳐 사용자 인터페이스를 보여줍니다<strong>(12)</strong>. </p>
<p>와, 정말. 요청한 웹페이지 화면을 빠르게 보여주기 위해 ms를 다툰다는걸 감안하면 정말 많은 과정이 한 순간 사이에 이루어 지는 것 같습니다(웹브라우저가 URL 구조를 해석하는 과정, 그리고 DNS 서버 사이의 송,수신 등은 간략하게 정리하였는데, 이는 다른 포스팅에서 조금 더 자세하게 이야기를 나누면 좋을 것 같습니다 :) ). 하지만 이것으론 부족합니다. 제가 궁금한 것은 서버가 어떻게 브라우저를 구분하는지 입니다.</p>
<hr>
<h1 id="tcpip-프로토콜을-사용한-송수신">TCP/IP 프로토콜을 사용한 송,수신</h1>
<h2 id="osi-7-layer와-tcp-protocol">OSI 7 Layer와 TCP Protocol</h2>
<p>위 그림의 HTTP ~ TCP 사이, 그러니까 5번과 10번, &amp; 9번과 6번 부분을 이미지를 통해 조금 자세히 보겠습니다.
<img src="https://velog.velcdn.com/images/vlv_3/post/5db14cb6-e9e5-40e7-b9c3-1f7d865e0edd/image.jpeg" alt="">
OSI 7 Layer와 TCP/IP Protocol은 모두 네트워크에서 통신이 일어나는 (같은)과정을 단계별로 도식화한 것입니다. <strong>통신 과정을 계층별로 나눈 이유는 통신 과정을 단계별로 파악하기에 용이하기 때문</strong>입니다. 네트워크 통신 과정은 높은 단계에서 낮은 단계로, 또는 낮은 단계에서 높은 단계로 계층별로 순서대로 일어나기 때문에, 만약 문제가 발생하면 어떤 단계에서 문제가 일어났는지 파악하기 쉽다는 장점이 있습니다.</p>
<p>TCP/IP Protocol은 OSI 7 Layer와 계층을 대부분 공유하고 있고, 대부분의 네트워크가 이와 같은 프로토콜을 사용하고 있기 때문에 TCP/IP 프로토콜을 통해 네트워크 통신 과정을 이해하는 것이 더 바람직하다고 보는 견해도 있습니다만, 추상적인 구분일 뿐 계층을 공유하는 각 단계가 수행하는 것은 거의 동일하므로 저는 OSI 7 Layer를 통해 보다 자세히 이 과정을 살펴보려 합니다.</p>
<h3 id="1-physical-layer">1. Physical Layer</h3>
<p>이 계층은 비트, 즉 0, 1을 통해 데이터를 전송받거나 또는 외부로 데이터를 전송하는 역할을 수행합니다. 이름과 같이 소프트웨어와 같은 가상의(virtual) 것이기 보다 현실세계에 존재하는 물리적 장비, 즉 통신 케이블, 허브(컴퓨터 사이의 네트워크 연결), 리피터(노이즈로 인해 망가진 신호를 복원하거나 신호를 증폭하는 역할을 수행하지만, 현재는 대부분 허브로 대체되었습니다)등에 의존합니다. </p>
<h3 id="2-datalink-layer">2. DataLink Layer</h3>
<p><strong>데이터링크 레이어는 직접 연결된 두개의 노드 사이의 데이터 전송(node-to-node data transfer)기능을 수행합니다.</strong> 이 계층에서 노드란 스위치를 가리킨다고 봐도 무방합니다(2계층에 위치한 스위치라는 뜻에서 L2 switch라고도 합니다). 이 계층에서 스위치는 여러 역할을 수행하는데요. 예를 들어 스위치의 특정 포트를 활성 또는 비활성하는 기능, 특정 포트의 서비스 품질(Quality of Service) 조정, 네트워크 트래픽 모니터링을 위한 포트 미러링 설정 등이 있습니다. </p>
<p>L2 스위치의 가장 중요한 기능 중 하나는 MAC 필터링 및 액세스 제어 기능입니다. 사용자가 웹 브라우저를 통해 HTTP 요청 메세지를 서버를 향해 보내면, 이 요청 메시지는 4계층인 Transport Layer, 3계층인 Network Layer를 거쳐 패킷이라는 일정한 크기를 가진 데이터 전송의 한 단위로 변화하는데요. 패킷은 DataLink Layer에서 Frame이라는 패키지로 감싸지게 됩니다. Frame은 하나의 패킷과 출발지의 MAC Address, 그리고 도착지의 MAC address로 구성되어 있습니다. *<em>IP 주소가 때론 변할 수 있는, 클라이언트와 서버라는 최종 목적지들의 주소라면, MAC address란 절대 변하지 않고, (굳이 의도한 것이 아니라면)세계에서 유일한, 다른 컴퓨터(스위치)간 데이터를 전송하기 위한 물리적 주소입니다. *</em>
<img src="https://velog.velcdn.com/images/vlv_3/post/1b71ab93-ee36-430e-81c0-4e4d9c6f6bb0/image.png" alt=""></p>
<pre><code>앞의 노란색 공간은 LIC 생산자를, 뒤의 파란색 공간은 이 LIC의 고유한 시리얼 넘버를 가리킵니다.</code></pre><p>Frame을 택배상자에 비유하면, 패킷은 전달할 물건, MAC address는 간선 상, 하차가 이루어질 터미널 주소에 비유할 수 있을 것 같습니다. 만약 중간중간 MAC address를 따라 여러 스위치(또는 라우터의 hop)를 거칠 필요 없이 한번에 서버-클라이언트 간 네트워크 통신이 가능하다면 어떨까요? 최종 목적지에 도달하기 위해 가까운 다음 스위치의 주소를 찾는 과정을 반복할 필요가 없으니, 아마도 이것이 더 빠르고, 리소스를 덜 소비할 것 같습니다. <strong>그러나 서버-클라이언트 간 데이터 송수신 경로가 하나뿐이라면, 그 경로가 단절될 경우 송수신이 불가능해질 것입니다.</strong> MAC address를 따라 스위치간 Frame을 전송하는 방식은 유일한 경로에 대한 의존성을 분산시킬 수 있습니다. 또 IP 주소는 가변성을 가질 수 있기 때문에, 한 번 목표 네트워크 통신을 성공적으로 마무리지었다고 해서 영구히 그 IP 주소를 사용한 통신이 의도한 목적지와의 커뮤니케이션 결과물이라는 보장이 있을 수 없습니다. 반대로 MAC 주소만으로 네트워크 통신을 하는 것도 불가능에 가까운데, MAC 주소는 반영구적이기 때문입니다. MAC 주소간 통신만 존재한다면 MAC 주소 저장공간이 무한히 늘어나야겠지요.</p>
<p>다시 IP와 MAC 주소를 사용한 네트워크 통신 이야기로 돌아오겠습니다. L2 또는 L3 스위치가 패킷 내부의 비밀스런 정보를 모두 볼 필요는 없겠지요(택배기사분께서 택배의 내용물을 들여다 보지 않아도 물건을 전달할 수 있는 것처럼요). 스위치 간 네트워크 통신을 위해 필요한 정보는 출발지의 MAC address, 도착지의 Mac address, 그리고 출발지 및 도착지의 IP 주소 뿐입니다. 그런데 문제가 있습니다. <strong>내 IP 주소와 서버 IP 주소, 그리고 내 MAC 주소는 알고 있습니다. 그런데 수많은 중간 목적지 사이에서 내 다음 목적지의 MAC 주소는 어떻게 알 수 있을까요?</strong></p>
<p>본격적으로 경로 탐색을 시작하기 전에, <strong>우선 목표 서버가 같은 네트워크 안에 있는지, 또는 그렇지 않은지 구분해야 합니다</strong>. 지금 필요한 것이 바로 목표 서버의 IP 주소와 나의 IP 주소, 그리고 서브넷 마스킹을 위한 서브넷 주소(255.255.255.0)입니다. 서브넷 주소는 두 아이피 주소 각각 앞의 몇 자리(즉 Network ID)가 서로 일치하는지 확인하기 위해 필요합니다. 만약 두 아이피 주소의 NetWork ID가 일치한다면 목표 서버는 같은 네트워크 안에 있다고 할 수 있습니다. 그렇다면 내 다음 목적지의 MAC 주소를 찾는 일은 L2 스위치 사이의 관계가 될 것입니다. 만약 그렇지 않다면, 내 L3 스위치(현재는 라우터와 같다고 봐도 좋을 것 같습니다)는 외부 네트워크의 다른 L3 스위치와 모종의 커뮤니케이션을 수행합니다. 둘을 구분하여, 아래에서 자세히 보시겠습니다.</p>
<p>우선** 목표 서버가 같은 네트워크 안에 있는 경우<strong>입니다. 아시다시피 스위치에 연결된 컴퓨터는 하나가 아닙니다. Frame이 스위치에 전송되면, 스위치는 MAC 주소 테이블을 확인합니다. 만약 출발지 MAC 주소가 등록되어 있지 않다면, 내 MAC 주소를 Port와 함께 등록합니다. 그리고 목표 서버의 IP 주소와 매칭되는 MAC 주소가 스위치의 MAC 주소 테이블에 존재하는 경우, 목표 서버의 MAC 주소를 반환하면 끝입니다. 만약 MAC 주소가 존재하지 않는 경우, 스위치에 연결된 &quot;모든&quot; 컴퓨터에게 ARP 요청 메시지를 전송합니다. 이것을 Flooding이라고 합니다. ARP 요청 메시지를 수신한 컴퓨터 중, 목표 서버 IP와 Network ID가 일치하는 컴퓨터는 자신의 MAC 주소를 스위치로 반환합니다. 스위치는 목적지의 MAC 주소를 MAC 주소 테이블에 저장하고, 마침내 클라이언트는 스위치로부터 전달받은 서버의 MAC 주소를 확인한 뒤 통신을 시작합니다. **이러한 절차를 ARP Protocol(Address Resolution Protocol, 주소 결정 프로토콜)이라고 부릅니다.</strong></p>
<h3 id="3-network-layer">3. Network Layer</h3>
<p>만약 <strong>목표 서버가 같은 네트워크 안에 존재하지 않는다면</strong> 어떨까요? 외부 네트워크로 나가야 하니, 이젠 L2 스위치만이 아닌 3계층에 위치한 L3 스위치간의 송수신 문제를 포함합니다. 단일한 네트워크 안에서 MAC address로 경로를 찾는 것은 위에서 서술하였으니, 이번엔 L3 스위치(이하 라우터)간의 통신에 대해 더 살펴보겠습니다.</p>
<p>동일 네트워크 내부에 목표 서버가 존재하지 않는다면, 패킷은 <strong>기본 게이트웨이(Default Gateway)</strong>로 향합니다. 기본 게이트웨이는 내 컴퓨터가 속한 네트워크의 라우터를 말합니다. 어떤 패킷이던 자체 네트워크가 아닌 외부 네트워크에 도달하려면 반드시 게이트웨이를 거쳐야 합니다. 라우터 단위로 구성된 하나의 네트워크는 역시 다른 네트워크와 통신하기 위해 그 네트워크와 연결된 다른 라우터와 송수신합니다 <em>(실제로 라우터가 네트워크의 한 단위는 아닙니다. 하나의 라우터는 여러개의 네트워크를 가지고 있을 수도 있습니다. 여기서는 쉽게 내용을 전달을 위해 한 라우터가 하나의 네트워크를 다른 네트워크와 구분하는 구분자라고 가정해보겠습니다. 웹이 무수한 라우터의 집합이라고 비유하기 위해 필요한 적절한 가정일 것 같네요 ㅎㅎ)</em>. 이 라우터와 라우터간 데이터(패킷) 전달은 한 번에 모든 경로를 이동함으로써 끝나는 것은 아닙니다. 인접 라우터까지 경로를 지정한 뒤, 인접 라우터로부터 최적 경로를 파악하여 최적 경로에 위치한 다른 인접 라우터로 패킷을 전송합니다. 이를 <strong>홉-바이-홉(hop-by-hop) 라우팅</strong>이라고 하고, 인접 라우터를 <strong>넥스트 홉(next hop)</strong>이라고 부릅니다. <strong>문제는 최적 경로를 따라 넥스트 홉을 지정하는 것입니다</strong>. </p>
<p>각각 라우터는 <strong>라우팅 테이블(Routing table)</strong>이라는 저장소를 가지고 있습니다. 라우팅 테이블에는 여러 목적지 주소(IP)와 넥스트 홉의 IP 주소 등 경로 정보가 저장되어 있습니다. 라우터는 몇 가지 방법을 통해 이 최적 경로 정보를 수집하고 라우팅 테이블을 만듭니다. 서브넷 마스크를 사용해 네트워크 아이디가 일치하는(또는 가장 유사한) 라우터를 넥스트 홉으로 삼을 수도 있고요(<strong>다이렉트 라우팅</strong>). 개발자가 직접 특정 라우터를 넥스트 홉으로 삼도록 지정할 수도 있습니다(<strong>스태틱 라우팅</strong>). 특정 라우터를 무조건 넥스트 홉으로 지정하도록 하는 것은 지나치게 경로 의존성을 가지게 할 우려가 있습니다. 때문에 우회 경로를 찾는 로직이 필요한 경우도 있습니다(<strong>다이나믹 라우팅</strong>). 라우터가 점점 많아지고, 또 서브넷의 사용으로 인해 목표 서버의 IP와 완전히 일치하는 라우터를 찾기 어려운 경우도 있습니다. 때문에 <strong>신뢰성이 높은 라우팅 방법으로 얻어낸 주소별로 가중치를 둘 수도 있습니다</strong>. 다이렉트 라우팅, 스태틱 라우팅, 그리고 다이나믹 라우팅 순서로 우선순위를 가질 수 있습니다.</p>
<h4 id="packet">Packet</h4>
<p><img src="https://velog.velcdn.com/images/vlv_3/post/deda815f-1442-4db4-b7ed-17b8b6cc4ef5/image.png" alt="">
앞서 패킷에 대하여 계속 이야기하고 있는데요. <strong>패킷은 데이터 전송의 최소 단위이면서, 동시에 출발지와 도착지의 IP주소 등을 포함하고 있습니다</strong>. 패킷의 헤더에는 IP의 버전을 가리키는 Version 필드, 라우팅할 때 서비스 우선순위를 정할 수 있는 Type-of-Service 필드, 패킷 분열 여부를 감지하는 Fragment identifier 필드, 목표 라우터에 도달하지 못할 것으로 판단되어 소멸되기 전에 미리 라우터를 건너뛸 수 있는 홉의 최대 한도를 정할 수 있는 <strong>Time-to-Live 필드</strong>(라우터를 건너뛸 때마다 하나씩 감소하여 0이되면 홉을 중단합니다)등 여러 옵션 정보가 담겨있습니다. 패킷의 Payload에 조각난 데이터가 담기게 되는데요. 페이로드의 최대 크기는 1460바이트로, 굉장히 작은 데이터 조각에 불과하지만, 이렇게 작은 조각을 여러 번 보냄으로써 커다란 데이터를 한번에 보냄으로써 발생할 수 있는 트래픽을 예방할 수 있습니다. 하드웨어 성능이 폭발적으로 증가하고 있는 지금은 패킷의 페이로드 크기를 더 늘리는 것도 가능할 것 같은데... 아마도 기존 네트워크와 충돌을 우려해서 일까요? :)  </p>
<h3 id="4-transport-layer">4. Transport Layer</h3>
<p>커다란 데이터 덩어리 대신 패킷으로 데이터를 주고 받기 위해서는 우선 커다란 데이터를 잘게 쪼개는 작업이 필요할 것입니다. 상위 계층에서 HTTP 프로토콜을 통해 포장된 데이터는 4계층에서 네트워크 통신을 위한 작은 데이터 단위로 쪼개집니다. 모든 데이터가 쪼개지는 것은 아니고, 데이터가 <strong>최대 전송 유닛(Maximum Transmission Unit, MTU, 최대 1518Bytes)</strong>보다 큰 경우로 한정됩니다. MTU가 큰 용량은 아니기 때문에, 상당한 경우 데이터는 패킷 전송을 위해 쪼개져야 하는데요. 이 쪼개진 데이터에 TCP 헤더 또는 UDP 헤더가 붙은 것을 <strong>세그먼트(segment)</strong>라고 부릅니다. TCP 프로토콜과 UDP 프로토콜 모두 4계층에서 사용될 수 있는데요. 둘은 용도에 따라 다르게 쓰일 수 있습니다. 두 프로토콜을 구분하는 가장 큰 특징 중 하나는 <strong>일련 번호(Sequence number)</strong>, <strong>확인 응답 번호(Acknowledgment number)</strong>, <strong>윈도우 크기(Window size)</strong>등의 정보가 헤더에 담겨있는 지 여부입니다. UDP 헤더에는 상기한 정보가 포함되어있지 않기때문에 데이터 전송 속도 제어(Flow control), 패킷을 담아두는 버퍼가 넘치지 않도록 하는 혼잡 제어(Congestion control)등이 불가합니다. 즉 <strong>UDP 프로토콜은 TCP 프로토콜과 달리 데이터 전송의 신뢰성(Reliable transmission)을 보장하기 어렵습니다.</strong> 단 TCP 프로토콜은 반드시 목적지를 지정해야 하지만 UDP 프로토콜은 그렇지 않기 때문에, UDP 프로토콜이 브로트캐스트(Broadcast, 로컬 네트워크 내부의 컴퓨터에게 데이터를 일괄 전송하는 것. ARP Protocol에서도 언급했었죠?)에 더 적합하다고 볼 수 있습니다. </p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/vlv_3/post/db6df8c1-2f97-4f57-a83c-dbee2c836565/image.png" alt="TCP 헤더의 구조">  TCP 헤더의 구조</p>
</blockquote>
<p>세그먼트는 Transport Layer 내부의 가상 공간인 <strong>TCP Buffer</strong>에 차곡차곡 쌓입니다. 앞서 패킷 하나가 네트워크 통신의 주체인 것 처럼 계속 이야기해왔지만, 하나의 패킷(세그먼트 + IP 헤더)마다 한번의 송신을 하는 것은 비효율적이기 때문에, 정해진 크기의 TCP Buffer에 세그먼트가 충분히 쌓이면 이 덩어리를 송수신합니다. 수신자는 한번에 받을 수 있는 정보 덩어리의 크기 정보를 TCP 헤더에 담아 보내는데, 이 크기를 윈도우 크기(Window size)라고 합니다. 송신자는 헤더에 담인 윈도우 크기를 보고 윈도우 크기만큼 한번에 정보 덩어리를 송신합니다. 수신자는 이 덩어리를 잘 받았다면, 송신자에게 <strong>확인 응답 번호(Acknowledgment number)</strong>를 보냅니다. 확인 응답 번호는 수신자가 받은 데이터 덩어리의 가장 끝 일련 번호(Sequence number)에 1을 더한 값입니다. 송신자는 확인 응답 번호를 받을 때 까지 대기하고 있습니다. 확인 응답 번호를 받은 송신자는 데이터 덩어리가 성공적으로 전송됐다고 믿고, 다음 시퀀스의 데이터 덩어리를 전송할 준비를 합니다. 윈도우 크기에 맞게 데이터 덩어리를 송수신하는 것은 네트워크 지연을 예방하기 위해 중요한 요건이 됩니다. 따라서 윈도우 크기를 고정하는 것 보다, 문제없이 전송이 이루어지면 윈도우 크기를 늘리거나, 중간에 데이터가 유실되거나 문제가 발생하면 윈도우 크기를 줄이는 등 유동적인 크기 조절이 보다 효율적인 데이터 전송을 가능하게 할 것입니다. 이를 <strong>Sliding window</strong>라고 부릅니다. 이 유동적인 윈도우 크기는 TCP Buffer 내부의 여유공간과 관련이 있습니다. 받은 세그먼트들을 호스트가 빠르게 처리해 TCP buffer를 빠르게 비우는 것도 트래픽 예방을 위해 중요한 요건 중 하나일텐데요. TCP Buffer 내부의 세그먼트들은 상위 계층의 응용 어플리케이션과 TCP 소켓을 매개로 상호작용합니다. <strong>TCP 소켓</strong>은 상위 계층(User mode)에서  TCP/IP 계층(Kernel mode)에 접근할 수 있는 추상적 인터페이스로서, 일종의 파일입니다. 읽고(req), 쓰는(res.send) 등의 작업이 소켓 파일 내부에서 이루어집니다. TCP 소켓은 IP 주소와 Port 주소를 더한 <strong>소켓 주소(Socket Address)</strong>를 갖는데, 해당 소켓 주소(그리고 해당 소켓)는 해당 호스트 내에서 유일하며 또 일시적(Emphemeral)입니다. 아무튼, 소켓을 통해 응용 어플리케이션이 충분히 빠른 시간 안에 데이터 덩어리를 소화하지 못하면 TCP buffer안에 세그먼트들은 점점 쌓일 것이고, 결국 통신 지연을 유발할 것입니다. 흥미로운 것은 이 Buffer 내 데이터 누적 현상은 데이터를 송신받고 난 다음에 일어나는 현상이라는 점인데요. 네트워크 통신 지연은 서버와 클라이언트 간 데이터 전송 지연만이 원인이 아닐 수 있다는 점을 꼭 기억할 필요가 있을 것 같습니다.</p>
<h3 id="5-session-layer">5. Session Layer</h3>
<p>이제부터는 TCP/IP 프로토콜로 말하자면 응용 계층입니다. 이 계층에선 TCP/IP 세션을 만들거나 없에는 역할을 수행합니다. 세션은 두 계층간 통신을 정의하기 때문에, 통신의 방향성을 정하는 역할도 수행합니다. 통신의 방향성은 <strong>전이중 통신</strong>(Full Duplex, 두 계층 사이에 일방향 통신선이 각각의 방향으로 하나씩, 두 개 존재합니다. 데이터 송수신이 동시에 가능합니다), <strong>반이중 통신</strong>(두 계층 사이에 양방향 통신선이 하나 존재합니다. 때문에 한쪽 계층이 송신 시 다른쪽에선 수신만 가능합니다), <strong>동시 송수신 방식</strong>(Duplex, 일방향 통신선이 하나 존재합니다) 등으로 정할 수 있습니다. </p>
<h3 id="6-presentation-layer">6. Presentation Layer</h3>
<p>이 계층에서는 사용자의 명령어를 완성하거나 결과를 표현해 줍니다. 데이터를 포장하거나, 압축하거나, 암호화합니다.</p>
<h3 id="7-application-layer">7. Application Layer</h3>
<p>네트워크 소프트웨어의 UI 부분으로서, 사용자 I/O의 접점이기도 합니다.</p>
<hr>
<h1 id="서버는-어떻게-ip가-같은-두-브라우저를-구분할까">서버는 어떻게 IP가 같은 두 브라우저를 구분할까?</h1>
<p>처음의 질문을 기억하시나요? &quot;서버는 어떻게 IP가 같은 두 브라우저를 구분할까?&quot;. IP가 같다면, IP 외 다른 식별자가 필요할 것입니다. 그 식별자는 바로 포트 주소였습니다. 포트 주소는 네트워크 서비스나 특정 프로세스를 구분하는 논리적 단위로, 16bit 크기를 가지고 있습니다. 즉 서로 다른 2^16개의 포트 번호가 존재할 수 있습니다. 잘 알려는 포트 번호로는 22번(SSH), 53번(DNS), 80번(WWW HTTP) 등이 있습니다. 0번~ 1023번 포트는 잘 알려진 포트(Well-known port), 1024번 ~ 49151번은 등록된 포트(Registered Port), 49152번 ~ 65535번은 동적 포트(Dynamic Port)라는 구분 기준으로 나눌수도 있습니다. 주의해야 할 점은 Server Socket의 최대 동시 연결 갯수가 포트 주소의 크기와 동일하지 않다는 부분인데요(만약 그렇다면 구글이나 네이버는 서버 동시 이용자 수 / 65535 개의 서버가 필요할 것입니다). <strong>소켓의 수는 리눅스 파일 디스크립터 수 만큼 생성이 가능하다</strong>고 합니다. 소켓의 본질은 파일이니까요!</p>
<hr>
<h2 id="출처">출처</h2>
<p>OSI 7 Layer, (1) <a href="https://github.com/BJS-kr/Records/blob/main/%EC%95%8C%EC%93%B8%EC%BB%B4%EC%9E%A1/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%EC%83%81%EC%8B%9D.md">https://github.com/BJS-kr/Records/blob/main/%EC%95%8C%EC%93%B8%EC%BB%B4%EC%9E%A1/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC_%EC%83%81%EC%8B%9D.md</a>, (2) <a href="https://shlee0882.tistory.com/110">https://shlee0882.tistory.com/110</a>, (3) <a href="https://ryusae.tistory.com/4">https://ryusae.tistory.com/4</a>
arp protocol, (1) <a href="https://musclebear.tistory.com/12">https://musclebear.tistory.com/12</a>, (2) <a href="https://www.stevenjlee.net/2020/06/07/%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-arp-address-resolution-protocol-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C/">https://www.stevenjlee.net/2020/06/07/%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-arp-address-resolution-protocol-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C/</a>, (3) <a href="https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Example">https://en.wikipedia.org/wiki/Address_Resolution_Protocol#Example</a>
router, <a href="https://catsbi.oopy.io/225439bd-ec84-4e16-aeca-0dfcb9954ea6">https://catsbi.oopy.io/225439bd-ec84-4e16-aeca-0dfcb9954ea6</a>
ip packet, <a href="https://mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-18%ED%8E%B8-IP-Header-IP%ED%97%A4%EB%8D%94-%EA%B5%AC%EC%A1%B0">https://mindnet.tistory.com/entry/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%89%BD%EA%B2%8C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-18%ED%8E%B8-IP-Header-IP%ED%97%A4%EB%8D%94-%EA%B5%AC%EC%A1%B0</a>
라우터와 L3 switch 구분, <a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=nackji80&amp;logNo=220228728915">https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=nackji80&amp;logNo=220228728915</a>
IP, <a href="https://choseongho93.tistory.com/199">https://choseongho93.tistory.com/199</a>
TCP, <a href="https://nesoy.github.io/articles/2018-10/TCP">https://nesoy.github.io/articles/2018-10/TCP</a>
슬라이딩 윈도우, <a href="https://4network.tistory.com/entry/windowsize">https://4network.tistory.com/entry/windowsize</a>
포트번호, <a href="https://ko.wikipedia.org/wiki/%ED%8F%AC%ED%8A%B8_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%82%B9)">https://ko.wikipedia.org/wiki/%ED%8F%AC%ED%8A%B8_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%82%B9)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[예비 개발자 일기 2. 좋은 동료가 되고싶다!!]]></title>
            <link>https://velog.io/@vlv_3/%EC%98%88%EB%B9%84-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%9D%BC%EA%B8%B0-2.-%EC%A2%8B%EC%9D%80-%EB%8F%99%EB%A3%8C%EA%B0%80-%EB%90%98%EA%B3%A0%EC%8B%B6%EB%8B%A4</link>
            <guid>https://velog.io/@vlv_3/%EC%98%88%EB%B9%84-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EC%9D%BC%EA%B8%B0-2.-%EC%A2%8B%EC%9D%80-%EB%8F%99%EB%A3%8C%EA%B0%80-%EB%90%98%EA%B3%A0%EC%8B%B6%EB%8B%A4</guid>
            <pubDate>Sun, 02 Oct 2022 11:38:25 GMT</pubDate>
            <description><![CDATA[<p>항해99 프로그램은 매주 3~4명으로 이루어진 새로운 팀을 구성한다. 2주차에서 3주차로 넘어가는 길목에서, 많은 사람을 만나본 것은 아니지만, 내가 만나본 사람들은 크게 두 부류로 나뉘어졌다. 더 가까워지고 싶은 사람과 더는 그렇지 않은 사람. </p>
<blockquote>
<p>야 공부는 혼자하는거야! <em>- 모 자기계발서 중</em></p>
</blockquote>
<p>개발을 공부하는 누구나 더 실력있는 개발자가 되길 바란다. 우리 9기 팀원들이 하루 12시간 이상 컴퓨터 앞에 붙어있는 이유도 그와 같을 것이다(특히 내가 속한 반은 40여명 정도인데, 매일 밤 12시 너머서도 80~ 90%는 공부하고 있다. 열의가 정말 대단하다... 무섭다 ㄷㄷ). 그러나 모든 동료들이 같은 베이스라인을 가지고 시작한 것도 아니고, 같은 속도로 발전하지도 않는다. 누구는 더 앞서 나가는 반면, 다른 누군가는 (적어도 지금은)더디게 전진한다. 비단 동료들 사이에서 경쟁할 것 뿐만 아니다. 나를 포함해 동료 대부분을 차지하는 비전공자들은 취업전선에서 어리고 좋은 대학을 졸업한 전공자들과 같은 선에서 겨루어야 한다. 때문에 같은 시간 공부하더라도 더 잘 공부하는 것이 중요하다고 생각했다.</p>
<h4 id="더-잘-공부하기-위해선-어떻게-해야할까">더 잘 공부하기 위해선 어떻게 해야할까.</h4>
<p>나는 몇 가지 리스트를 만들어 스스로 답하려고 했다. 나는 왜 공부하는가? 항해99가 끝나고 무엇을 하고 싶은가? 내가 원하는 것을 달성하기 위해서 무엇을 공부해야 하는가? 언제까지, 얼마나 내 자원을 쏟을 것인가? 플랜이 실패할 경우 대안이 있는가? 각자 처한 상황이 다르기에 잘 공부하기 위한 고민도 다르겠지만, 내 경우는 이정도로 정리할 수 있겠다(적어도 지금은. 히히). </p>
<p><strong>나는</strong> <strong>스타트업 기업에서 개발자로서 첫 직장을 가지기로 목표를 잡았다</strong>. 언젠가는 완전히 새롭거나 더 나은 시스템을 창출하여, 정당한 댓가를 받고 시스템 사용자들에게 더 개선된 서비스를 제공하는 경험을 하고 싶기 때문이다. 스타트업 기업에 취직하기 위해 내 기준에서 가장 빠른 길은, 대학교, 대학원을 다닐 때 약간이나마 공부했던 개발 경험을 베이스로 웹 개발자가 되는 것이라고 판단했다. 어떤 스타트업이나 웹 개발자 수요는 있을테니까. </p>
<p>목표를 정했다면 무슨 공부에 초점을 맞추는게 유리할지 선택해야한다. 조사 결과, 스타트업이 수요하는 초년생 개발자는 <strong>프론트엔드에 가까운 풀스택 개발자</strong>일 것이라고 판단했다. 그런데 <strong>어불성설</strong>, 불과 몇 개월만에 그런 양질의 개발자가 되기는 누구에게나 쉽지 않을 것이다. 나와 같은 비전공자는 더더욱 그럴거고. 때문에 공부 범위를 더 좁혀야했다. 열심히 공부하는 것은 누구나 하는 것이고, 나는 더 유니크한 존재여야 경쟁력이 있을 것이라고 생각했다.</p>
<p><strong>나는 우선 백엔드를 공부하기로 정했다</strong>. 프론트엔드는 지금 당장은 초년생 개발자의 스타트업 취업에 조금이나마 더 유리할지 몰라도, 트렌드 변화가 매우 빠르기 때문에 지금 공부한 것이 프론트엔드 개발자가 되고 나서 언제까지 트렌드로 남아있을 지 예측하기 더 어려울 것 같았다. 물론 백엔드 개발자도 같은 고민을 하기는 마찬가지지만, 장기적으로 풀스택 개발자가 되기 위해서 둘 중 하나를 먼저 선택하야 한다면 내게는 백엔드가 적합하다고 생각했다. 개인적인 성향이 눈에 보이는 부분보다 보이지 않는 뒷면에서 데이터를 다루는 것을 더 선호하기 때문이기도 했고, 크기를 확장하는 단계에서 스타트업 기업이 마주하는 중대한 마찰중 하나는 눈에 보이는 부분보다 보이지 않는 서버 문제라고 생각했기 때문이다(물론 눈에 보이지 않는 문제를 해결하기 위해서는 좋은 실력이 필요하므로 초년생 개발자에게 적합한 태스크가 아닐 수 있지만, 그 부분은 안고 가야할 문제이다. 더 나은 개발자가 되기 위해 노력할 뿐.. ㅋㅋ ㅜㅜ). 무엇보다 백엔드에서 프론트로 가는 것이 내 자신에게 동기부여하기에 더 유리할 것이라고 판단했다. <strong>프론트를 하기에 나는 아직 프론트가 무엇인지, 무엇을 할 수 있고 없는지 그 범주가 다소 막연하게 느껴졌기 때문이다</strong>.</p>
<p><strong>NodeJs는 백엔드를 선택한 내게 좋은 선택지가 되었다</strong>. 자바스크립트를 사용하기에 가뜩이나 넉넉치 않은 시간 자원을 고려할 때 새로운 언어를 배우지 않아도 된다는 점이 좋았고, 단일 쓰레드 이벤트 루프를 가지므로 로직이 간단하거나 작고 빈번한 요청을 처리하는 케이스가 더 많은 스타트업 기업에게 보다 적합하다고 생각했다. 방대한 npm 패키지를 사용하여 빠른 개발에 용이하다는 점도 한 몫 했다. Nodejs를 배우기로 하면서, 2022년 현재 가장 많이 사용되고 있는 프레임워크들중 일부인 Next.js, Nest.js, Nuxt.js 중 적어도 하나를 공부하기로 마음먹었다. Nuxt.js는 당장은 고려 대상이 아니었고, React와 친한 Next.js와 Express에서 뻗어나온 Next.js중 하나를 선택하려 한다. Next.js는 CSR인 React에 SSR을 더해 UX를 개선할 수 있으니 좋았고, 프론트엔드 개발자와 협업에도 도움이 될 것 같았다 (강의도 상당히 많다).반면 Nest.js의 경우 지금도 많이 사용되고 있는 Express를 기반한다는 점, flow와 더불어 한창 뜨고있는 typescript를 공부할 수 있다는 점, (타입스크립트를 반드시 쓰기 때문에)다른 개발자와 협업에 유리한 점, 의존성 주입(Dependency Injection)을 바탕으로 클래스 생성 및 소멸 주기를 개발자가 별도로 파악하지 않아도 되는 경우가 있다는 점 등을 장점으로 이해했다. 둘 중 하나를 선택하려면 추상적 개념이해 말고 실제로 써봐야할 것 같아서, 이 선택 고민은 현재 진행중이다. 설사 이 기술로 당장 정해진 기간 내(마음 속으론 내년 초까지) 취업에 실패하더라도, 내겐 그동안 공부한 코딩 기술이 남아있을 것이니 다시 도전할 수 있다.</p>
<p>항해99를 시작한지 3주차에 들어선 지금, 웹 개발자가 되기로 처음 마음먹었을 때부터 지금까지 내가 가진 간절함이 변하진 않았지만, 다른 동료들과 협업 경험을 하면서 새롭게 체감하는 것은, <strong>혼자하는 공부와 여럿이서 개발하는 것은 다르다는 점</strong>이다. </p>
<p>1주차에서 <strong>첫 미니프로젝트를 동료들과 같이 진행하면서 협업 부분에서</strong> <strong>시행착오를 많이 겪었다</strong>. 당시에 내가 팀장이었는데, 업무 분담을 제대로 하지 못해서 누군가는 자신의 한도 이상으로 과중한 업무를 배정받아 과부화된 반면, 다른 누군가는 백엔드 개발이 아니라 웹 디자인만 계속 담당하게 되어 공부할 기회를 놓쳤다. 깃헙도 사용할 줄 몰라서 각자 코딩한 것을 한 사람이 모두 받아 변수 이름부터 함수 기능까지 수정해야했다. 거기다 능력과 시간 한도를 충분히 고려하지 않고 기획을 처음부터 크게 잡아 결국 시한 내에 기획한 모든 기능을 구현하지 못했다. 팀원 모두가 손해를 보았다.  </p>
<p><strong>실패로부터 얻은 교훈도 있었다</strong>. 팀원과 진행사항을 주기적으로 공유할 것, 기획은 혼자 틀어쥐고 좌지우지하지 말것, 실행 가능한 가장 작은 단위를 구현하는 것부터 살을 붙여 나갈 것, 계획 변경에 대비할 수 있는 여유 시간을 항상 남겨 놓을 것, 트러블슈팅 기록을 팀 노션에 공유하면 나중에 거기서 배울게 생긴다는 것 등... 정리하면 팀원들과 상호간 커뮤니케이션이 개발 공부할 때 정말 중요하다는 것이었다. 비록 그게 개발과 관련된 이슈가 아니라 <strong>시시한 농담이나 일상적인 이야기일 지라도, 커뮤니케이션 능력이 좋은 팀원과 같이 공부하는 것은 항상 즐겁다</strong>. 팀 모두가 즐겁게 공부할 수 있는 환경을 제공하는 능력은 특출난 개발 실력만큼이나 대단한 것이다. </p>
<p>3주차에 들어서면서, 내게 중요한 이슈는 개발 실력을 키우는 것 뿐만 아니라 <strong>다른 팀원들에게 좋은 동료가 되는 것</strong>이다. 새롭게 배운것을 서로 공유하고, 인간적으로 동료를 존중하는 사람, 마음에 여유가 있는 사람이 되자. 팀원들이 더 가까워지고 싶은 사람이 되자 다짐했다. :)</p>
<hr>
<p>ES5/6에 대해서는 다음 포스팅에서 정리하겠습니다. :)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[empty, undefined, & null 구분하기]]></title>
            <link>https://velog.io/@vlv_3/empty-undefined-null-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@vlv_3/empty-undefined-null-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 01 Oct 2022 12:07:13 GMT</pubDate>
            <description><![CDATA[<p>empty, undefined, 그리고 null 모두 변수가 비어있음을 표시하는 식별자이다. 그러나 이들은 반복문을 통해 몇 가지 특징에 따라 구분될 수 있다.</p>
<pre><code class="language-javascript">let arr = [undefined, 0]
arr[3] = 2

arr.forEach((v, i) =&gt; console.log(v, i)) // undefined 0 / 0 1 / 2 3
arr.map((v, i) =&gt; v + i) // [NaN, 1, &lt;1 empty item&gt;, 5]
arr.filter(v =&gt; !v) // [undefined, 0]
arr.reduce((a, c, i) =&gt; a+c+i) // NaN

const arr3 = new Array(2)
console.log(arr3) // [emptyx2]</code></pre>
<p>첫째, empty는 반복문을 통하여 객체를 순회할 때 잡히지 않는다. empty는 변수가 선언되기 이전 단계에서 개념적으로 존재하는 것으로 어떤 주소도 가리키지 못한다. 예를 들어 존재하지 않는 arr의 2번 인덱스나, Array만 선언한 상태인 arr3과 같은 경우와 같다. 또한 arr.filter 부분을 보면 알 수 있듯이, empty는 undefined, 0이 false를, 2가 true를 가리키는 것과 달리 그 자체로 false나 true를 가리키지 않는다.  </p>
<p>둘째, undefined는, 개발자가 의도적으로 undefined 값을 할당하는 경우 반복문을 통해 객체를 순환할 때 값이 할당된 변수로써 순회 대상이 되지만, 변수를 초기화만 하고 값을 할당하지 않은 상태와 같이 자바스크립트 엔진이 자동적으로 undefined를 할당하는 경우에서는 순회 대상이 되지 않는다. 때문에 의도적으로 undefined를 할당하는 것은 예기치 못한 오류를 발생시킬 수 있다. 따라서 이를 지양해야하고, 대신 null과 같은 표현을 같은 의도로 선택할 수 있을 것이다. </p>
<pre><code class="language-javascript">console.log(typeof null) // Object
console.log(null == undefined) // true
console.log(null === undefined) // false</code></pre>
<p>셋째, null의 타입을 받아올 때 아래와 같이 object type을 반환하므로 주의해야 한다. 과거 자바스크립트 버전의 버그이지만, 과거 버전을 기반으로 빌드된 여러 웹페이지가 여전히 잘 사용되고 있어 건드리지 못하고 있는 상태이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JWT와 API]]></title>
            <link>https://velog.io/@vlv_3/JWT%EC%99%80-API</link>
            <guid>https://velog.io/@vlv_3/JWT%EC%99%80-API</guid>
            <pubDate>Sun, 25 Sep 2022 13:44:33 GMT</pubDate>
            <description><![CDATA[<p>JWT(Json Web Token)은 서버가 어떤 클라이언트에게, 어떤 정보를 제공할 수 있을지 서버가 판단할 수 있도록 만들기 위한 고안의 결과물중 하나이다. 서버는 많은 정보를 다룰 수 있지만, 어떤 정보는 모두에게 공개될 수 있는 반면 어떤 정보는 어떤 누군가에게만 제공되어야 하고, 다른 누군가에게 제공되어선 안된다. 가령 특정성있는 정보가 외부에 노출되면 곤란할 것이다. 때문에 서버는 클라이언트 요청이 정당한 요청인지 판단해야 하고, 미리 정해진 정보만 제공할 수 있어야 한다. 클라이언트 Cookie에 인증 정보 일부를, 서버에 그 다른 한 쪽 부절인 Session을 저장하고 클라이언트 요청이 들어올 때마다 비교할 수도 있지만, JWT를 사용하는 방식은 서버에 클라이언트를 위한 session 공간을 유지할 필요가 없고, session 내부에 클라이언트 정보를 유지할 필요도 없다. 이런 특성은 RESTful API에 가까운 구현에 유리할 뿐 아니라 서버 리소스도 덜 잡아먹는다는 잇점이 있다. 다만 access token 노출시, 토큰이 유효한 시간동안 정보 유출의 여지가 있다는 점에서, access token과 refresh token의 유효기간을 정하기 위해 클라이언트의 편의와 정보 유출의 위험 사이에서 적절한 선택을 필요로 한다는 점이 한계일 수 있다. 또한 서버사이드에서 클라이언트의 인증 상태에 개입하기 어렵다는 단점이 있다. 이는 Session &amp; Cookies를 사용한 방식과 마치 거울 관계에 있다고 볼 수 있을 것이다.</p>
<p>(웹상의)API(Aplication Programming Interface)는 주로 서버와 상호작용하기 위한 인터페이스를 가리킨다. API와 라이브러리, 프레임워크, 그리고 SDK는 때로 유사한 개념처럼 말해지기도 한다. 엄밀한 개념정의가 아닌 필요한 경우가 아닌 한, 이들을 구분하는 데서 오는 실익은 크지 않을 수 있다는 생각이 든다(둘로 구분하자면 API와, API를 기반으로 만들어지는 라이브러리 등일 수 있을 것이다). 1주차 과정에서 카카오맵 API를 사용하여 주변 헬스장 정보를 받아와 보여주는 코드를 짜는 경험을 했었는데, 단순히 몇줄의 코드만 추가하면 유용한 정보를 받아와 내 코드에 구현할 수 있다는 것에 그 유용성을 크게 느낄 수 있었다. 회사 입장에서는 서버 내부 정보를 공개하고 API를 제공함으로서 부수적인 수입을 노릴 수 있을 것이고, 사용자는 적은 시간 투자로 개발자 개인이 얻을 수 없는 대용량의 (비)가공된 정보를 얻을 수 있는 것이다. 다만 API 서비스 제공자가 정한 방식으로만 서버 정보를 사용할 수 있어서 API 사용자가 개발가능한 서비스의 범주가 한정적일 수 있다는 점이 유일한 아쉬운 점이었다. 구글, 메타, 테슬라등 거대 기업의 광대한 사용자 경험 데이터의 축적은 막대한 자산이며 (그것이 공개되기만 한다면) 개발 범주의 폭이 매우 넓어지게 될 것이다(공개되기만 한다면!). 이것을 앞으로 어떻게 내가 활용할 수 있을지, 너무너무 기대된다. :)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript와 데이터 타입 - 느슨한 타입의 동적 언어]]></title>
            <link>https://velog.io/@vlv_3/Javascript%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85-%EB%8A%90%EC%8A%A8%ED%95%9C-%ED%83%80%EC%9E%85%EC%9D%98-%EB%8F%99%EC%A0%81-%EC%96%B8%EC%96%B4</link>
            <guid>https://velog.io/@vlv_3/Javascript%EC%99%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%83%80%EC%9E%85-%EB%8A%90%EC%8A%A8%ED%95%9C-%ED%83%80%EC%9E%85%EC%9D%98-%EB%8F%99%EC%A0%81-%EC%96%B8%EC%96%B4</guid>
            <pubDate>Fri, 23 Sep 2022 10:58:58 GMT</pubDate>
            <description><![CDATA[<p> 코딩과 글쓰기 사이에는, 오랜 시간 의자에 앉은 채로 타자기를 두들기는 작업이라는 점 외에도 많은 공통점이 있겠지만, 여기선 단어와 문장을 사용한다는 것을 언급하고 싶다. 단어와 문장은 어떤 개념의 표상(Representation)이다. 가령 어떤 특정한 개념을 여러 나라의 언어로 표현할 수 있지만 그것이 가리키는 대상이 하나인 것 처럼, 단어와 문장은 어떤 개념을 표현하기 위한 껍데기와 같다. 그런 면에서 변수에 값을 할당하는 과정과 어떤 단어에 어떤 개념을 매칭시키는 사회적 합의 과정은 유사한 점이 있다. 다만 전자는 유한한 메모리 크기 안에서 관계도를 그리는 반면, 후자는 무한한 마음 속 공간에서 매칭이 이루어진다는 차이가 있다. 따라서 코딩은 어떤 정보를 어느 정도 크기의 빈 메모리 공간에 할당해야 할 지 정하는 것이 중요하다. </p>
<h1 id="데이터-타입이란">데이터 타입이란</h1>
<p> 데이터 타입(Data type)은 데이터를 할당하기 위해, 그 <strong>변수가 가지는 메모리 공간의 크기 정보를 담고있는 표현 형식</strong>이다. 자바 스크립트의 경우 변수를 선언하면, 컴퓨터는 그 값의 데이터 타입을 판별한 뒤 그에 맞는 메모리 공간을 확보한다 예를 들어 int 데이터 타입의 어떤 변수는, 4byte의 메모리 공간을 할당받으므로, -2,147,483,648 부터 2,147,483,647까지의 정수 정보를 가질 수 있다. 반면 char 데이터 타입의 경우 1byte, 즉 256개의 알파벳 등을 구분할 수 있다. int와 char와 같은 데이터 타입을 기본 타입(Primitive Type)이라고 부른다. </p>
<h2 id="자바스크립트의-데이터-타입---원시-타입과-참조-타입">자바스크립트의 데이터 타입 - 원시 타입과 참조 타입</h2>
<h4 id="원시-타입primitive-type">원시 타입(Primitive Type)</h4>
<p>  자바스크립트의** 원시 타입은, 참조 타입과 달리, 하나의 변수는 자신에게 할당된 데이터 공간에 하나의 데이터를 가지고 있다**. 이 변수는 새로운 데이터를 가질 수 있으며, 기존의 데이터를 가리키는 주소와 변수 간의 링크가 사라졌을 뿐 기존의 데이터 자체는 여전히 메모리 공간에 남아있다(immutablity). 또 변수를 재할당해도 원시 타입 변수의 데이터를 받아온 다른 원시 타입 데이터의 값은 변하지 않는다. </p>
<pre><code class="language-javascript">let a = 2;
let b = a;

a = 3;

console.log(a) // a = 3
console.log(b) // b = 2</code></pre>
<p>  자바스크립트의 원시 타입에는 다음과 같은 카테고리가 존재한다.</p>
<p>| 데이터 타입 | 형태 | 메모리의 크기 | 표현 가능 범위 |
|:----------|:----------:|----------:|
| Boolean 타입 | True, False  |  1bit| |
| Null 타입 | null | 1byte | |
| Undefined 타입 | undefined | - | |
| Number 타입 | - | 8byte(정수/소수 구분이 없음) | |
| String 타입 | - |  한 문자 당 2byte| |</p>
<p>그 외에도 객체의 식별자를 대신할 수 있는 Symbol Type과, Number Type이 다룰 수 없는 매우 크거나 작은 수를 표현할 수 있는 Bigint Type이 존재한다.</p>
<p>조사 과정에서 원시 타입과 관련된 몇 가지 궁금한 점을 여기 정리해둔다. </p>
<p>첫 번째는 Null 타입과 Undefined 타입의 메모리 할당이다. <strong>Null type은 존재하지 않는(nothing) 값, 알 수 없는 값(unknown)이 할당되어 있음</strong>을 가리킨다. Undefined type은 변수에 값이 할당되어 있지 않은 상태를 가리킨다. 두 타입은 의미가 상당히 유사하지만, 자바스크립트는 이 두 타입을 구분하여 사용하고 있는데, 두 타입의 구분 기준 중 하나는 초기화 여부이다. </p>
<pre><code class="language-javascript">const a; // 변수 선언(Declaration)
const b = 2; // 변수 정의(Definition)</code></pre>
<p> 변수의 선언 단계에서 자바스크립트는 그 변수에 undefined를 할당한다. 이 단계에서 변수는 메모리 상의 어떤 주소를 가리키고 있지만, 그 메모리 공간은 초기화되지 않은 상태에 있다. 반면 null을 할당받은 변수는, 초기화된 후의 빈 메모리 공간을 갖는다. 이때 null 타입의 변수가 갖는 빈 메모리 공간의 크기는 1byte이다. 또 한가지 사소한 차이점은 null은 개발자가 의도한 데이터의 부재 상태를 가리키는 반면, undefined는 호이스팅 과정과 같이 개발자의 의도와 관계 없이 자바스크립트가 선언(또는 정의)한 것일 가능성이 크다는 점이다.</p>
<p> 두 번째는 String 타입의 주소 할당 문제이다. 자바스크립트는 문자당 2byte의 데이터 공간을 할당하지만, 문자가 모인 문장이 정의된 문자열 변수는 하나이다. 이 문자열 변수는 모든 문자의 주소 공간을 가리키고 있을까? 그렇지 않다. 문자열 변수와 매칭된 주소는 문자열의 시작 문자의 주소이며, 문자열의 끝에 null값을 추가함으로써 문자열의 끝을 알린다. 따라서 <strong>한 문자는 2byte의 공간을 갖지만, 한 문자열은 그 총합에 더해 1byte를 추가로 갖는다</strong>. </p>
<h3 id="참조-타입reference-type">참조 타입(Reference Type)</h3>
<p>참조 타입이란 원시 타입을 제외한 모든 타입을 가리킨다. 원시 타입 변수가 값을 가리키는 주소 정보를 가진다면, <strong>참조 타입 변수는 다른 변수를 가리키는 주소 정보를 가진다</strong>. 때문에 참조 타입 변수가 가리키는 다른 타입의 변수를 재할당할 경우, 값이 담긴 주소를 참조한 모든 참조 타입 변수가 가리키는 값 또한 달라진다. </p>
<pre><code class="language-javascript">let a = {
    value : &#39;a&#39;
}
let b = a // 얕은 복사(Shallow copy)

console.log(b.value) // b.value = a
b.value = &#39;b&#39;
console.log(a.value) // a.value = b
</code></pre>
<p>이런 참조 타입의 특징 때문에, 참조 타입 변수를 복사하는 방법은 얕은 복사(shallow copy)와 깊은 복사(deep copy) 두 가지로 나뉜다. <strong>얕은 복사는 원본 참조 변수와의 관계가 유지되는 경우, 깊은 복사는 연결 관계가 완전히 끊어진 경우를 가리킨다</strong>. 흔히 그 편의성으로 인하여 클라이언트-서버 사이의 데이터 전송시 JSON.stringify 함수와 JSON.parse 함수를 사용하는데, 이는 대표적인 깊은 복사의 사례이다. </p>
<p>자바스크립트의 참조 타입에는 다음과 같은 카테고리가 존재한다.</p>
<table>
<thead>
<tr>
<th align="center">데이터 타입</th>
<th align="center">주 사용 목적</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Object 타입</td>
<td align="center">동적 데이터 저장 및 전송</td>
</tr>
<tr>
<td align="center">Array 타입</td>
<td align="center">동적 데이터 저장 공간</td>
</tr>
<tr>
<td align="center">Date 타입</td>
<td align="center">정해진 날짜, 시간 반환</td>
</tr>
<tr>
<td align="center">Function 타입</td>
<td align="center">메서드를 담고있는 인스턴스 생성</td>
</tr>
<tr>
<td align="center">Primitive wrapper 타입</td>
<td align="center">원시 값 조작을 위한 참조 타입</td>
</tr>
</tbody></table>
<p>흥미로운 점은 자바스크립트가 Function을 참조 타입 변수에 포함시키고 있다는 점이다. 여기서 function 타입의 변수는 실질적인 메서드를 가리키는 주소 정보를 가지고 있다.</p>
<h3 id="javascript-형변환">JavaScript 형변환</h3>
<p>자바스크립트의 원시 타입 변수인 number, string, 그리고 boolean은 서로 형변환이 가능하다. 때로 형변환은 개발자의 의도에 따라 명시적으로 일어날 수도 있으나, 산술 연산자의 사용과 같이 암묵적으로 일어날 수도 있다.</p>
<pre><code class="language-javascript">let a = 1
let b = 2
console.log(a + b) // number 3

let c = &quot;c&quot;
console.log(a + c) // string &quot;1c&quot;

//string을 만나는 순간 형변환이 일어납니다.
console.log(a + b + c) // string &quot;3c&quot;
console.log(c + b + a) // string &quot;c21&quot;</code></pre>
<p>명시적 형변환의 용례는 다음과 같다.</p>
<pre><code class="language-javascript">
Number(&quot;123&quot;) // string &quot;123&quot; to number 123

ParseInt(“27”) // 27
ParseInt(0033); // 27 (8진수)
ParseInt(0x1b); // 27 (16진수)

ParseFloat(&quot;123.123&quot;) // number 123.123

String(&quot;123&quot;) // 123 to &quot;123&quot;

a = &quot;100&quot;
a.toString() // string &quot;100&quot;
a.toString(2) // string &quot;1100100&quot;
              // 인자로 param진수로 변환하여 반환한다.

b = 100.123
b.toFixed(2) // string &quot;100.12&quot;
            // 소수점 param자리까지 올림

Boolean(100) // true
Boolean(&quot;1&quot;) // true
Boolean([]) // true

Boolean(null) // false
Boolean(undefined) // false
Boolean() // false</code></pre>
<hr>
<h1 id="느슨한-타입loosely-typed의-동적dynamic-언어">느슨한 타입(loosely typed)의 동적(dynamic) 언어</h1>
<p>자바스크립트는 변수 선언 시 형(type)을 개발자가 지정해주어야 하는 자바, 또는 C와 같은 강 타입(또는 정적 타입)언어와 구분되는 약 타입(또는 동적 타입, 느슨한 타입) 언어이다. 약 타입 언어인 자바스크립트는 변수 선언 단계에서 의도적인 형 지정을 요구하지 않아 강 타입 언어보다 편의성에서 강점이 있다. 또한 자바스크립트는 다양한 변수 타입을 Number, String, Boolean 등으로 간추려 형변환이 요구되는 부분에서도 상대적으로 쉽게 의도하는 것을 구현할 수 있도록 하고있다.</p>
<p>시간이 다르게 하드웨어 성능이 개선되는 현 시대에서 형변환의 필요성은 다소 감소한 부분이 있다는 점에서 자바스크립트의 동적 타입 할당은 그 강점이 계속 증가하고 있다고 볼 여지가 있다. 그러나, 모든 웹 사용자가 양질의 하드웨어를 사용할 수 없다는 점, 클라우드 서버의 메모리 관리는 곧 기업의 순수익과 직결된다는 점, 빠른 데이터 전달을 통한 사용자 경험의 향상, 다른 언어 기반 프로그램과의 연동성, SQL DB 사용시 용이한 점 등 여전히 개발자가 변수 타입을 지정하는 것은 필수적이다. 때문에 자바스크립트도 탄생 이후 개발자에게 변수 타입을 조정할 수 있는 선택권을 부여할 필요성에 대하여 꾸준히 목소리가 있어왔다. </p>
<p>Typescript와 Flow는 자바스크립트 개발자에게 이러한 선택권을 부여해준다. 그러나 Typescript와 Flow는 사용법이나 구동 방식 등에서 차이가 있다. Typescript는 개발자가 미리 변수 타입을 지정해야 하고, 이것은 변수 타입 추론을 기본적으로 지원하는 Flow에 비해 비교적 엄격하다. 또 (비록 정확하게 일치하는 표현은 아니지만)Typescipt로 짜인 코드를 자바스크립트로 컴파일하는 과정을 거치는 것과 비교해 Flow는 자바스크립트로 짜인 코드 위에서 어노테이션처럼 작동한다. 때문에 기존에 자바스크립트 기반으로 서비스하고 있다가 타입 지정이 가능한 다른 언어로 넘어갈 때 Flow가 Typescript보다 장점이 있다고 볼 여지가 있다. 다만 Typescript는 facebook 내부의 특정 문제를 해결하기 위해 개발된 Flow보다 광범위하고 장기적인 목적의식과 개발 플랜을 따를 여지가 크다고 보는 견해가 존재한다<a href="https://medium.com/@constell99/%EC%9A%B0%EB%A6%AC%EA%B0%80-typescript%EB%A5%BC-%EC%84%A0%ED%83%9D%ED%95%9C-%EC%9D%B4%EC%9C%A0-b0a423654f1e">참조</a>. VSCode, Angular, Reddit 등이 Typescript를 사용하고 있는 대표적인 사례이다. </p>
<hr>
]]></description>
        </item>
    </channel>
</rss>