<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>배우자고</title>
    <link>https://endgame1019.tistory.com/</link>
    <description>안녕하세요 잘오셨어요</description>
    <language>ko</language>
    <pubDate>Mon, 25 May 2026 14:09:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>컴퓨터와 하나가 된다.</managingEditor>
    <item>
      <title>Google BigQuery REST API</title>
      <link>https://endgame1019.tistory.com/8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;서비스 개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Service&lt;/b&gt;: &lt;code&gt;bigquery.googleapis.com&lt;/code&gt;&lt;br /&gt;&lt;b&gt;Endpoint&lt;/b&gt;: &lt;a href=&quot;https://bigquery.googleapis.com&quot;&gt;https://bigquery.googleapis.com&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Discovery Document&lt;/b&gt;: &lt;a href=&quot;https://bigquery.googleapis.com/$discovery/rest?version=v2&quot;&gt;보기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google BigQuery REST API는 데이터를 생성, 관리, 공유, 쿼리할 수 있는 강력한 데이터 플랫폼입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 API 리소스 요약&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  v2.datasets&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;HTTP 요청&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;delete&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /bigquery/v2/projects/{projectId}/datasets/{datasetId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;지정 데이터셋 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;get&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/datasets/{datasetId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;데이터셋 정보 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;insert&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/projects/{projectId}/datasets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;빈 데이터셋 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;list&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/datasets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;프로젝트의 모든 데이터셋 나열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;patch&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PATCH /bigquery/v2/projects/{projectId}/datasets/{datasetId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;데이터셋 정보 부분 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;undelete&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/projects/{projectId}/datasets/{datasetId}:undelete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;삭제 복구(윈도우 내)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;update&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT /bigquery/v2/projects/{projectId}/datasets/{datasetId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;전체 정보 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/rest/v2/datasets&quot;&gt;datasets API 문서&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  v2.jobs&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;HTTP 요청&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;cancel&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/projects/{projectId}/jobs/{jobId}/cancel&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;작업 취소 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;delete&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /bigquery/v2/projects/{projectId}/jobs/{jobId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;작업 메타데이터 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;get&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/jobs/{jobId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;작업 정보 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getQueryResults&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/queries/{jobId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;쿼리 결과 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;insert&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/projects/{projectId}/jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;비동기 작업 시작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;list&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/jobs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모든 작업 목록 나열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;query&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/projects/{projectId}/queries&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;동기 쿼리 실행 및 결과&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs&quot;&gt;jobs API 문서&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  v2.models&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;HTTP 요청&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;delete&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /bigquery/v2/projects/{projectId}/datasets/{datasetId}/models/{modelId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모델 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;get&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/datasets/{datasetId}/models/{modelId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모델 정보 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;list&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/datasets/{datasetId}/models&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모든 모델 목록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;patch&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PATCH /bigquery/v2/projects/{projectId}/datasets/{datasetId}/models/{modelId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;모델 일부 필드 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/rest/v2/models&quot;&gt;models API 문서&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  v2.projects&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;HTTP 요청&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getServiceAccount&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/serviceAccount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;서비스 계정 정보 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;list&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;프로젝트 목록 나열&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/rest/v2/projects&quot;&gt;projects API 문서&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  v2.routines&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;HTTP 요청&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;delete&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /bigquery/v2/projects/{projectId}/datasets/{datasetId}/routines/{routineId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;루틴 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;get&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/datasets/{datasetId}/routines/{routineId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;루틴 정보 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;getIamPolicy&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/{resource=projects/*/datasets/*/routines/*}:getIamPolicy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IAM 정책 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;insert&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/projects/{projectId}/datasets/{datasetId}/routines&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;새 루틴 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;list&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /bigquery/v2/projects/{projectId}/datasets/{datasetId}/routines&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;루틴 목록 나열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;setIamPolicy&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/{resource=projects/*/datasets/*/routines/*}:setIamPolicy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IAM 정책 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;testIamPermissions&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /bigquery/v2/{resource=projects/*/datasets/*/routines/*}:testIamPermissions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IAM 권한 테스트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;update&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT /bigquery/v2/projects/{projectId}/datasets/{datasetId}/routines/{routineId}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;루틴 전체 정보 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/rest/v2/routines&quot;&gt;routines API 문서&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기타&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;v2.rowAccessPolicies: 테이블 행 접근 정책 관리&lt;/li&gt;
&lt;li&gt;v2.tabledata: 테이블 데이터 삽입 및 조회&lt;/li&gt;
&lt;li&gt;v2.tables: 테이블 생성, 관리, 수정, 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/rest&quot;&gt;BigQuery REST API 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/libraries&quot;&gt;BigQuery 클라이언트 라이브러리&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/bigquery/docs/reference/api-uploads&quot;&gt;API 업로드 가이드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>bigquery</category>
      <category>DataWarehouse</category>
      <category>GCP</category>
      <category>google</category>
      <category>load</category>
      <category>구글</category>
      <category>구글클라우드플랫폼</category>
      <category>데이터엔지니어</category>
      <category>데이터웨어하우스</category>
      <category>빅쿼리</category>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/8</guid>
      <comments>https://endgame1019.tistory.com/8#entry8comment</comments>
      <pubDate>Sat, 11 Oct 2025 09:44:23 +0900</pubDate>
    </item>
    <item>
      <title>Airflow ETL 환경 구축</title>
      <link>https://endgame1019.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* uv 기반 가상환경 사용&lt;/b&gt;&lt;br /&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;도커 환경 (MySQL)&lt;/h2&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker run -d \
  --name airflow-mysql \
  -e MYSQL_ROOT_PASSWORD=0000 \
  -e MYSQL_DATABASE=airflow_db \
  -e MYSQL_USER=airflow \
  -e MYSQL_PASSWORD=airflow_pass \
  -p 3306:3306 \
  mysql:8.0&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MySQL 예제 쿼리 및 데이터 입력&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE TABLE users (
    user_id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    country VARCHAR(50)
);

INSERT INTO users (user_id, name, age, country) VALUES
  (1, 'Alice', 25, 'US'),
  (2, 'Bob', 32, 'KR'),
  (3, 'Charlie', 29, 'JP'),
  (4, 'David', 40, 'US'),
  (5, 'Eunji', 27, 'KR');&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 docker run 명령어로 MySQL 8.0 컨테이너를 생성하고, 예제 쿼리로 users 테이블과 샘플 데이터를 구성했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ETL_Project&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;​&lt;br /&gt;[ETL_tutorial GitHub 바로가기]&lt;br /&gt;(&lt;a href=&quot;https://github.com/Jindongleee/ETL_tutorial#etl_project&quot;&gt;https://github.com/Jindongleee/ETL_tutorial#etl_project&lt;/a&gt;)&lt;br /&gt;​&lt;br /&gt;Extract(운영DB, 데이터포털, SNS) &amp;rarr; Transform(Pandas, Spark ML, Spark, Hadoop) &amp;rarr; Load(S3, AWS Redshift, GCP BigQuery, HBase) 사용예정&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;airflow 보안 이슈 .env 사용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;​&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;cd [project 위치]
export $(cat .env | xargs)   # airflow.cfg 파일 lite 버전 덮어씌우기 위함
airflow db migrate
airflow api-server --port
airflow triggler
airflow dag-processor&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;​&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반드시 가상환경과 파일이 ~/airflow/dags 에 존재해야 함(감시 위치)&lt;/li&gt;
&lt;li&gt;위 명령어 안쓰고 airflow standalone 일단 사용 예정&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;​&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Flow&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;-- Airflow --
Mysql dump &amp;rarr; transform (pandas) &amp;rarr; load to s3 &amp;rarr; copy s3 to redshift&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Airflow Connection 설정&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;​&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Mysql_default&lt;/b&gt;&lt;br /&gt;(docker create &amp;rarr; mysql, MacOS/Ubuntu &amp;rarr; default IPv6 따라서 localhost가 아닌 IPv4 127.0~로 설정)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host: 127.0.0.1&lt;/li&gt;
&lt;li&gt;port: 3306&lt;/li&gt;
&lt;li&gt;db: ariflow_db&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AWS_S3_default, IAM (AmazonS3FullAccess 생성)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS Access Key ID = IAM Access Key&lt;/li&gt;
&lt;li&gt;AWS Secret Access Key = IAM Secret Key&lt;/li&gt;
&lt;li&gt;추가 필드 JSON:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ &quot;region_name&quot;: &quot;ap-northeast-2&quot; }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;redshift_default, IAM role 생성\&lt;i&gt;\&lt;/i&gt; (AmazonS3ReadOnlyAccess, 퍼블릭 모드로 전환 필요)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;host: redshift 작업그룹 엔드포인트&lt;/li&gt;
&lt;li&gt;User: 네임스페이스 user&lt;/li&gt;
&lt;li&gt;pwd: 네임스페이스 작업 토글 관리자 보안&lt;/li&gt;
&lt;li&gt;port: 5439&lt;/li&gt;
&lt;li&gt;db: 네임스페이스 DB 이름&lt;/li&gt;
&lt;li&gt;추가 필드 JSON:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{ &quot;sslmode&quot;: &quot;require&quot; }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과 Redshift &amp;rarr; query editor v2&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;830&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkQYjR/btsQ49qWCzh/PtL0XJcZxpJxRX0uQyVit0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkQYjR/btsQ49qWCzh/PtL0XJcZxpJxRX0uQyVit0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkQYjR/btsQ49qWCzh/PtL0XJcZxpJxRX0uQyVit0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkQYjR%2FbtsQ49qWCzh%2FPtL0XJcZxpJxRX0uQyVit0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1654&quot; height=&quot;830&quot; data-origin-width=&quot;1654&quot; data-origin-height=&quot;830&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;*다음 시간에는 Transform을 Transform에 대해 자세히 다뤄보도록 하겠습니다&lt;br /&gt;\&lt;/i&gt; 질문에 답변 달아드립니다!&lt;/p&gt;</description>
      <category>airflow</category>
      <category>Amazon</category>
      <category>eTL</category>
      <category>MySQL</category>
      <category>redshift</category>
      <category>S3</category>
      <category>데이터 엔지니어</category>
      <category>데이터 엔지니어링</category>
      <category>재밌따</category>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/7</guid>
      <comments>https://endgame1019.tistory.com/7#entry7comment</comments>
      <pubDate>Wed, 8 Oct 2025 11:58:51 +0900</pubDate>
    </item>
    <item>
      <title>데이터 웨어하우스 및 Airflow 자동화</title>
      <link>https://endgame1019.tistory.com/6</link>
      <description>&lt;h1&gt;  Airflow를 활용한 ETL 자동화: MySQL &amp;rarr; S3 &amp;rarr; Redshift&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 파이프라인 자동화를 위한 Airflow 기반 ETL 구축 튜토리얼&lt;/b&gt;&lt;br /&gt;(UI 연동 직전까지 전체 구조 완성)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1️⃣ ETL 개념 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ETL&lt;/b&gt;은 &lt;b&gt;Extract &amp;rarr; Transform &amp;rarr; Load&lt;/b&gt; 3단계를 거쳐 데이터를 수집하고 정제해 &lt;b&gt;데이터 웨어하우스(DWH)&lt;/b&gt;로 옮기는 과정입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2️⃣ 아키텍처 구조&lt;/h2&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;┌───────────┐    ┌────────────┐    ┌────────────┐    ┌──────────────┐
│ MySQL DB │ &amp;rarr; │  Extract   │ &amp;rarr; │ Transform  │ &amp;rarr; │  Load (S3)   │
└───────────┘    └────────────┘    └────────────┘    └──────────────┘
                                                         │
                                                         &amp;darr;
                                               ┌────────────────┐
                                               │ Redshift (DWH) │
                                               └────────────────┘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Airflow&lt;/b&gt;는 이 전체 파이프라인의 &lt;b&gt;스케줄링 및 자동화 오케스트레이션&lt;/b&gt;을 담당합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3️⃣ 사용 기술 스택&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Airflow 3.1.0&lt;/b&gt; : ETL 스케줄링 및 자동화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL&lt;/b&gt; : 운영 DB (데이터 원본)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AWS S3&lt;/b&gt; : 중간 데이터 저장소&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Amazon Redshift&lt;/b&gt; : 최종 데이터 웨어하우스&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Python (pandas)&lt;/b&gt; : 데이터 전처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4️⃣ Airflow 프로젝트 구조&lt;/h2&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;~/airflow/
├── airflow.cfg            # Airflow 설정 파일
├── dags/                  # DAG 파일 저장 폴더
│   └── etl_redshift.py    # ETL DAG 정의 파일
├── logs/                  # Task 로그
└── airflow.db             # Airflow 내부 DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;Airflow&lt;/b&gt;는 &lt;code&gt;airflow.cfg&lt;/code&gt;의 &lt;code&gt;[core] dags_folder&lt;/code&gt; 설정을 지속적으로 감시하며,&lt;br /&gt;해당 폴더의 &lt;code&gt;.py&lt;/code&gt; 파일에서 DAG 정의를 자동으로 등록합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5️⃣ .env 환경 설정&lt;/h2&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;IAM=arn:aws:iam:::role/...&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6️⃣ ETL DAG 코드 (etl_redshift.py)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Import 및 초기 설정&lt;/h3&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.providers.amazon.aws.hooks.s3 import S3Hook
from airflow.providers.postgres.hooks.postgres import PostgresHook
from airflow.providers.mysql.hooks.mysql import MySqlHook
from datetime import datetime
import pandas as pd
from dotenv import load_dotenv
import os

file_path = 'redshift_users.csv'
load_dotenv()
aws_iam = os.getenv(&quot;IAM&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Step 1: Extract (MySQL &amp;rarr; CSV)&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;def extract_mysql(**context):
    mysql = MySqlHook(mysql_conn_id='mysql_default')
    df = mysql.get_pandas_df('SELECT * FROM users;')
    df.to_csv(file_path, index=False)
    context['ti'].xcom_push(key='file_path', value=file_path)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Step 2: Transform (데이터 전처리)&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def transform_data(**context):
    file_path = context['ti'].xcom_pull(key='file_path', task_ids='extract')
    df = pd.read_csv(file_path)
    # 데이터 정제
    df[&quot;name&quot;] = df[&quot;name&quot;].str.upper()
    df = df.fillna({&quot;country&quot;: &quot;UNKNOWN&quot;})
    transformed_path = &quot;users_clean.csv&quot;
    df.to_csv(transformed_path, index=False)
    context['ti'].xcom_push(key=&quot;transformed_path&quot;, value=transformed_path)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Step 3: Load (S3 적재)&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def load_to_s3(**context):
    transformed_path = context['ti'].xcom_pull(key='transformed_path', task_ids='transform')
    s3 = S3Hook(aws_conn_id=&quot;aws_default&quot;)
    bucket_name = 'etl-tutorial-of-eddy'
    key = 'users/users_clean.csv'
    s3.load_file(filename=transformed_path, bucket_name=bucket_name, key=key, replace=True)
    context['ti'].xcom_push(key='s3_path', value=f's3://{bucket_name}/{key}')&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  Step 4: Copy (Redshift 적재)&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;def copy_to_redshift(**context):
    s3_path = context['ti'].xcom_pull(key='s3_path', task_ids='load')
    redshift = PostgresHook(postgres_conn_id='redshift_default')
    conn = redshift.get_conn()
    cur = conn.cursor()

    # 테이블 생성
    cur.execute(&quot;&quot;&quot;
        CREATE TABLE IF NOT EXISTS users(
            user_id INT,
            name VARCHAR(50),
            age INT,
            country VARCHAR(50)
        );
    &quot;&quot;&quot;)

    # S3에서 Redshift로 COPY
    copy_sql = f&quot;&quot;&quot;
        COPY users
        FROM '{s3_path}'
        IAM_ROLE '{aws_iam}'
        CSV IGNOREHEADER 1;
    &quot;&quot;&quot;
    cur.execute(copy_sql)
    conn.commit()
    cur.close()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  DAG 정의 및 의존성 설정&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;with DAG(
    dag_id=&quot;etl_redshift&quot;,
    start_date=datetime(2025, 10, 6),
    schedule=&quot;@daily&quot;,
    catchup=False
) as dag:

    extract = PythonOperator(task_id=&quot;extract&quot;, python_callable=extract_mysql)
    transform = PythonOperator(task_id=&quot;transform&quot;, python_callable=transform_data)
    load = PythonOperator(task_id=&quot;load&quot;, python_callable=load_to_s3)
    copy = PythonOperator(task_id=&quot;copy&quot;, python_callable=copy_to_redshift)

    # Task 의존성 정의
    extract &amp;gt;&amp;gt; transform &amp;gt;&amp;gt; load &amp;gt;&amp;gt; copy&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  다음 편 예고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;Airflow 내부 구조 완전 해부&lt;/b&gt; &amp;mdash; Scheduler, Triggerer, DagProcessor의 역할과 상호작용&lt;/p&gt;</description>
      <category>airflow</category>
      <category>MySQL</category>
      <category>redshift</category>
      <category>S3</category>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/6</guid>
      <comments>https://endgame1019.tistory.com/6#entry6comment</comments>
      <pubDate>Mon, 6 Oct 2025 23:48:53 +0900</pubDate>
    </item>
    <item>
      <title>공공데이터 API로 데이터 수집</title>
      <link>https://endgame1019.tistory.com/5</link>
      <description>&lt;h1&gt;&lt;strong&gt;  네이버 &amp;amp; 공공데이터 API 활용하기&lt;/strong&gt;&lt;/h1&gt;
&lt;h2&gt;&lt;strong&gt;1. 왜 API를 활용할까? (목적)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;데이터를 수집할 때 직접 크롤링을 하면 한계가 많다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;크롤링 대상 웹사이트가 변경되면 코드도 바꿔야 함&lt;/li&gt;
&lt;li&gt;로봇 차단, 속도 제한 등 제약 존재&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이때 API(Application Programming Interface)를 활용하면, 공식적으로 제공되는 데이터를 가져올 수 있다.&lt;/p&gt;
&lt;br&gt;

&lt;p&gt;예를 들어, 네이버 검색 API를 활용하면 뉴스, 블로그, 책 정보 등을 원하는 조건으로 가져올 수 있고, 공공데이터 API를 활용하면 정부·기관이 공개한 교통, 날씨, 환경 데이터를 쉽게 받아올 수 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;2. API의 기본 동작 원리 (개념)&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;API를 활용하는 절차&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;URL 구성&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API 문서에서 제시하는 형식에 맞게 요청 URL을 만든다.&lt;/li&gt;
&lt;li&gt;예: 검색어, 인증키, 옵션(페이지 수, 출력 건수 등)을 쿼리 파라미터로 붙임.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;요청(Request)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;requests.get() 또는 requests.post() 같은 방식으로 요청을 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;응답(Response)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버가 JSON 형식으로 데이터를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;파싱(Parsing)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JSON 데이터를 파이썬의 dict나 list로 변환해서 다룬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;활용&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;필요한 값만 뽑아 리스트/DB에 저장하거나 시각화·분석에 활용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;3. 간단한 예시 코드 (Python)&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import requests
import json  

# 네이버 뉴스 검색 API URL  
url = &amp;quot;https://openapi.naver.com/v1/search/news.json&amp;quot;  
headers = {  
    &amp;quot;X-Naver-Client-Id&amp;quot;: &amp;quot;YOUR_CLIENT_ID&amp;quot;,  
    &amp;quot;X-Naver-Client-Secret&amp;quot;: &amp;quot;YOUR_CLIENT_SECRET&amp;quot;  
}  

# 요청 보내기 (검색어: 데이터엔지니어)  
response = requests.get(url, headers=headers, params={&amp;quot;query&amp;quot;: &amp;quot;데이터엔지니어&amp;quot;})  

# 응답을 JSON 형태로 변환  
data = response.json()  

# 기사 제목만 추출해서 리스트에 담기  
titles = [item[&amp;quot;title&amp;quot;] for item in data[&amp;quot;items&amp;quot;]]  
print(titles)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 코드는 네이버 뉴스 검색 API를 활용하여 &amp;quot;데이터엔지니어&amp;quot;라는 키워드의 기사 제목만 뽑아오는 예시&lt;/p&gt;
&lt;p&gt;Client ID와 Client Secret을 네이버 개발자 센터에서 발급받아야 합니다.&lt;/p&gt;
&lt;br&gt;

&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;추가로 공부하면 좋은 것들&lt;br&gt; •    REST API 기본 구조 (GET, POST, PUT, DELETE)&lt;br&gt; •    JSON 데이터 구조 다루기 (dict, list, key/value 탐색)&lt;br&gt; •    requests 라이브러리 활용 심화 (status code, 에러 처리, timeout 설정)&lt;br&gt; •    공공데이터 API 인증키 발급 및 사용 방법&lt;/li&gt;
&lt;/ol&gt;</description>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/5</guid>
      <comments>https://endgame1019.tistory.com/5#entry5comment</comments>
      <pubDate>Thu, 2 Oct 2025 14:22:28 +0900</pubDate>
    </item>
    <item>
      <title>2025 멋쟁이사자처럼 해커톤 회고록</title>
      <link>https://endgame1019.tistory.com/4</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&lt;b&gt;모험의 순간 (MOMENT OF VOYAGE)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0426.JPG&quot; data-origin-width=&quot;3893&quot; data-origin-height=&quot;2188&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0ch2M/btsQoqAiLXm/T2olriVmIRJSAFD3jxwWG1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0ch2M/btsQoqAiLXm/T2olriVmIRJSAFD3jxwWG1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0ch2M/btsQoqAiLXm/T2olriVmIRJSAFD3jxwWG1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0ch2M%2FbtsQoqAiLXm%2FT2olriVmIRJSAFD3jxwWG1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;388&quot; data-filename=&quot;IMG_0426.JPG&quot; data-origin-width=&quot;3893&quot; data-origin-height=&quot;2188&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 멋쟁이사자처럼 13기 중앙해커톤이 끝났다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 사실 참가자로 참여하지 않았고 나의 부대표와 전체적인 관리와 애들의 질의응답을 위주로 맡았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이번 8월 해커톤이 당일이 오기전까지 서경대학교의 부대표로써 어떤 운영을 하고 어떠한 성장을 했는지 회고록을 좀 남겨보려고 한다. &lt;span style=&quot;font-family: GungSeo, serif;&quot;&gt;&lt;i&gt;(ㅎㅎ 하반기도 남긴 했지만 중앙해커톤이 멋사의 flower이니까~)&amp;nbsp;&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;010&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/010.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/010.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중앙해커톤을 하기 전에 기획이 한명 나가버리는 바람에 원래 계획했던 인원이 맞지 않아버렸다.. 초반에 총 기획 인원에 맞춰 아기사자를 뽑았는데 (중앙해커톤의 인원을 최대 7명까지 가능하다는 가정으로..) 기획이 나가버림으로써 공석이 생겨버렸다... 하지만 우리에겐 아이디어 뱅크 백엔드 아기사자 ㅅㅅㅎ씨 덕분에 (백엔드 + 기획 하이브리드 형 인간 정말 감사합니다) 운영이 되었고 정말 감사해요~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;열심히 팀 구성을 하고 결과를 보며 깨달은 점을 몇가지 적어보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;&lt;b&gt;배움과 모험&lt;/b&gt;&lt;/i&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 배움이라는 것은 결국 &amp;ldquo;결핍&amp;rdquo;에서 비롯된다고 생각한다. 모든 것이 완벽하게 준비되어 있고, 순리대로만 흘러간다면 배움의 자리는 줄어들 수밖에 없다. 오히려 어떤 자리가 비어 있을 때, 그 공백을 채우기 위해 모두가 힘을 모으고 누군가가 힘들어할 때 함께 도우며 문제를 해결해 나가는 그 순간 속에서 진정한 배움과 깨달음이 찾아온다. 그래서 나는 결핍이 단순한 부족함이 아니라 배움을 만들어내는 원동력이라고 여긴다. 이번 이야기를 꺼내게 된 계기도 사실상 팀 구성을 하면서 겪었던 경험 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작년에 내가 속해 있던 팀은 특이한 상황이었다. 운영진이 단 한 명도 없는, 중앙 해커톤 경험이 전혀 없는 아기사자들만으로 이루어진 팀이었다. 즉 우리에겐 &quot;운영진&quot; 부재의 결핍..&amp;nbsp; 처음 이 사실을 알았을 때는 솔직히 실망스러웠다. &amp;lsquo;아, 나보다 더 잘하는 운영진이 있으면 많이 배우면서 좋은 결과물을 낼 수 있을 텐데&amp;hellip;&amp;rsquo; 하는 아쉬움이 앞섰다. 그러나 시간이 멈춰주지 않는 이상 우리는 결국 우리만의 방식으로 문제를 풀어나가야 했다. 서버 배포부터 프로젝트 관리까지 발로 뛰어다니며 (ㄹㅇ 발로 뜀 누군가를 찾아가야됨) 부딪히고 배우는 과정은 쉽지 않았지만 그만큼 절실했고 그래서 더 빠르게 성장할 수 있었다. 지금 와서 돌이켜보면 만약 운영진이 있는 편안한 팀이었다면 나는 그렇게까지 간절하게 배움에 매달리지 않았을지도 모른다. 부족한 사람들이 모였기에 서로의 빈틈을 메우려는 노력이 배움으로 이어졌고 그 과정에서 성장의 가능성을 몸소 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;편하지도 않고, 안전하지도 않은 길을 택해 우리만의 목표를 향해 달려가는 것. 그것이야말로 모험이라고 생각한다. 다만 완전히 무모한 모험이 아니라, 어디선가 믿을 수 있는 끈이 조금이라도 존재해야 한다. 나는 대표단 활동을 하면서 그런 환경을 만들고 싶었다. 그래서 중앙 해커톤 팀을 구성할 때도 운영진이 없는 아기사자들끼리 팀을 꾸리게 했다. 물론 자칫 잘못하면 누군가는 불만을 품고 팀을 떠날 수도 있는 위험한 선택이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 우리는 더 일찍부터 준비했다. 단순히 해커톤을 위한 팀 구성이 아니라, 우리가 소속감을 가질 수 있는 기반을 마련해야 했다. 그래서 올해 2월부터 &amp;lsquo;우리만의 해자&amp;rsquo;를 만들었다. 여기서 말하는 해자는 멋쟁이사자처럼이라는 조직에 대한 소속감과 자부심이다. 구성원들이 &amp;ldquo;우리는 혼자가 아니고, 같은 가치를 공유하는 집단에 속해 있다&amp;rdquo;는 감각을 갖도록 다양한 활동을 준비했고, 그 노력이 결국 해커톤에서도 힘을 발휘했다. 그 결과, 무려 우리 학교에서 3팀이 예선에 진출했으며, 그중에는 아기사자들만으로 이루어진 팀도 있었다. 더 놀라운 건 본선까지 올라갔다는 사실이다. 이 성과는 단순히 대회 결과 이상의 의미를 가진다. 나는 내 나름의 배움과 성장을 챙길 수 있었고, 무엇보다 팀원들이 스스로 성장했다고 말해줄 때 그 어떤 보상보다 값진 기쁨을 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;&lt;b&gt;나만의 모험&lt;/b&gt;&lt;/i&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게 이런 환경을 만드는 일 자체가 하나의 모험이었다. 조직의 유지와 성장을 동시에 이끌어가려는 시도는 결코 안전한 선택이 아니었다. 언제든 실패할 수 있고, 팀이 와해될 수도 있는 위험을 내포하고 있었으니까. 하지만 나는 그 불완전함 속에서 진짜 배움이 태어난다고 믿었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;돌아보면, 2년 동안 멋쟁이사자처럼 활동을 하면서 아기사자 시절부터 부대표까지 위치에 따라 전혀 다른 깨달음을 얻을 수 있었다. 그 모든 경험이 지금의 나를 만들었고 함께 고민하고 결정해준 동료들 덕분에 가능했다. 특히 언제나 나와 뜻을 함께하며 무거운 고민을 나눠준 우리 대표(ㅅㅎㅇ)에게 진심으로 감사하다. 이 회고록을 마무리하며 그 고마움과 함께 이제는 새로운 모험을 향해 한 걸음을 내딛고자 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 서경대학교 13기 멋쟁이사자처럼 모두 사랑해  &lt;/p&gt;</description>
      <category>대표단</category>
      <category>멋사대학</category>
      <category>멋쟁이사자처럼</category>
      <category>새로운 경험</category>
      <category>서경대학교</category>
      <category>해커톤</category>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/4</guid>
      <comments>https://endgame1019.tistory.com/4#entry4comment</comments>
      <pubDate>Fri, 5 Sep 2025 23:30:22 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript 회원가입</title>
      <link>https://endgame1019.tistory.com/3</link>
      <description>&lt;h1&gt;JavaScript 회원가입/로그인/회원 조회 시스템&lt;/h1&gt;
&lt;p&gt;이번 포스팅은 &lt;strong&gt;JavaScript만으로 회원가입, 로그인, 회원정보 조회&lt;/strong&gt; 기능을 만드는 과제 예시입니다. 데이터베이스 없이 새로고침 전까지 가입정보를 배열에 저장하며 로그인·조회 기능을 제공합니다. (실제 서비스용이 아니라 학교 과제 목적이므로 참고용으로만 사용 바랍니다!)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;회원 정보 관리 객체 예시&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 사용자 관리 객체 (회원가입, 로그인, 회원정보 조회)
const UserManagement = {
  users: [],

  signUp() {
    let post_member = prompt(&amp;quot;아이디 비밀번호 이름을 입력하시오&amp;quot;);
    let words = post_member.split(&amp;#39; &amp;#39;);
    if (words.length !== 3) {
      alert(&amp;quot;올바른 형식으로 입력해 주세요 (아이디 비밀번호 이름)&amp;quot;);
      return;
    }
    const Post = { id: words[0], pw: words[1], name: words[2] };
    for (let i = 0; i &amp;lt; this.users.length; i++) {
      if (Post.id === this.users[i].id) {
        alert(&amp;quot;아이디 중복으로 인해 회원가입이 어렵습니다&amp;quot;);
        return;
      }
    }
    this.users.push(Post);
    alert(&amp;quot;회원가입이 완료되었습니다!&amp;quot;);
  },

  signIn() {
    let get_member = prompt(&amp;quot;아이디 비밀번호 입력하시오&amp;quot;);
    let words = get_member.split(&amp;#39; &amp;#39;);
    if (words.length !== 2) {
      alert(&amp;quot;올바른 형식으로 입력해 주세요 (아이디 비밀번호)&amp;quot;);
      return;
    }
    const Get = { id: words[0], pw: words[1] };
    for (let i = 0; i &amp;lt; this.users.length; i++) {
      if (Get.id === this.users[i].id &amp;amp;&amp;amp; Get.pw === this.users[i].pw) {
        alert(Get.id + &amp;quot;님 환영합니다.&amp;quot;);
        return;
      }
    }
    alert(&amp;quot;회원정보가 일치하지 않습니다.&amp;quot;);
  },

  getAllMembers() {
    let show = () =&amp;gt; {
      let out = &amp;quot;&amp;quot;;
      for (let i = 0; i &amp;lt; this.users.length; i++) {
        out += this.users[i].id + &amp;quot; &amp;quot; + this.users[i].name + &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;
      }
      return out;
    }
    document.getElementById(&amp;quot;findAll&amp;quot;).innerHTML = show();
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;주요 기능 버튼/태그 예시&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;회원가입:&lt;/strong&gt; &lt;code&gt;UserManagement.signUp();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;로그인:&lt;/strong&gt; &lt;code&gt;UserManagement.signIn();&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;전체회원조회:&lt;/strong&gt; &lt;code&gt;UserManagement.getAllMembers();&lt;/code&gt;&lt;br&gt;(id=&amp;quot;findAll&amp;quot;인 태그에 결과 출력)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;#html #css #javascript #signup #signin #과제&lt;br&gt;순수 JS로 객체/배열 조작하며 과제 UI 수행!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;</description>
      <category>html #css #javascript #signup #signin #get #post #find #과제</category>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/3</guid>
      <comments>https://endgame1019.tistory.com/3#entry3comment</comments>
      <pubDate>Mon, 3 Jun 2024 19:15:06 +0900</pubDate>
    </item>
    <item>
      <title>Comment(댓글) CRUD 설계</title>
      <link>https://endgame1019.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;회원, 게시물, 댓글 중 오늘은 댓글 &lt;b&gt;CRUD&lt;/b&gt;를 만들려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRUD는 Create(생성)Read(조회)Update(수정)Delete(삭제)로 이루어지는데 각 요소는 Post, Get, (Put, Patch), Delete에 매치된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;댓글 CRUD를 작성하기 위해서는 나는 편의상 Controller(Mapping) Service(dto 처리) Dto(실제 정보가 담긴 파일) Domain(멤버 변수)를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어노테이션과 멤버함수 변수의 종류와 역할을 설명하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤식으로 CRUD가 이루어지는지 알아보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Comment_domain.png&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;1854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ps53T/btsHDdQyOIb/juJFx6vA398FqMJduEipyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ps53T/btsHDdQyOIb/juJFx6vA398FqMJduEipyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ps53T/btsHDdQyOIb/juJFx6vA398FqMJduEipyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPs53T%2FbtsHDdQyOIb%2FjuJFx6vA398FqMJduEipyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;969&quot; height=&quot;1854&quot; data-filename=&quot;Comment_domain.png&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;1854&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Entity : 쉽게 생각해서 데이터베이스 중 하나의 테이블을 지칭하는 말이다. 따라서 여기서는 현재 Comment라는 테이블을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Getter, Setter : lombok 라이브러리를 통해 class내의 getter,setter을 만들지 않아도 사용가능한 어노테이션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Id: 현재 테이블의 기본키를 의미하며 여기서는 private Long id의 id가 기본키다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Column: 테이블을 이루는 하나의 열을 의미한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@ManyToOne, 여기는 없지만 (@OneToOne , @ManyToOne, @OneToMany) : 테이블간의 관계에는 일대일 다대다 일대다 다대일 등이 존재한다. 현재 ManyToOne은 다대일이라는 뜻을 의미하며 여기에는 포스팅하지 않았지만 앞서 나는 게시물과 회원정보의 Entity를 만들어 놓았다. 내가 작성중인 댓글 테이블은 게시물,회원정보 테이블의 id를 외래키로 참조하고 있으며 여러개의 댓글이 하나의 게시물에만 종속 되기 때문에 댓글과 게시물 관계는 ManyToOne(다대일) 관계가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@CreationTimestamp: Insert 쿼리가 발생할 때 현재 시간을 값으로 채워주는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@JoinColumn: 현재 키가 참조된 외래키라는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 각 멤버 변수가 무엇이 있는지 보면 (테이블을 이루는 키)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id(PK), member_id(FK), post_id(FK), content, commnetDate 다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Comment_Dto.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;1761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVYNed/btsHB9IgDKx/qVK7J15tOOSc0FSukt1yH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVYNed/btsHB9IgDKx/qVK7J15tOOSc0FSukt1yH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVYNed/btsHB9IgDKx/qVK7J15tOOSc0FSukt1yH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVYNed%2FbtsHB9IgDKx%2FqVK7J15tOOSc0FSukt1yH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1276&quot; height=&quot;1761&quot; data-filename=&quot;Comment_Dto.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;1761&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 파일은 댓글 파일의 Dto (CommentDto) Dto(Data Transfer Object) 즉 데이터 전달 객체를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수 전달하고자 하는 데이터만 담겨 있다. 따라서 Domain 파일의 정보들이 담기는 객체다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Comment_Service.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;2691&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpbahr/btsHBMfPmGD/DI4QeyTkyFcpjGdwRo6A21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpbahr/btsHBMfPmGD/DI4QeyTkyFcpjGdwRo6A21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpbahr/btsHBMfPmGD/DI4QeyTkyFcpjGdwRo6A21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbpbahr%2FbtsHBMfPmGD%2FDI4QeyTkyFcpjGdwRo6A21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1860&quot; height=&quot;2691&quot; data-filename=&quot;Comment_Service.png&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;2691&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 Service 파일로 데이터 처리를 담당하는 역할이다. 즉&amp;nbsp; 우리가 원하는 CRUD 요청의 처리를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 어노테이션을 통해 어떤 역할을 하는지 보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Serivce : 로직처리를 하기 class파일임을 명시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@AutoWired : 현재 사용중인 spring 프레임워크에서 의존성 주입을 위한 어노테이션이다. 객체간의 결합도를 낮추고 테스트와 유지보수를 용이하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Transcational: 트래잭션이란 어떤것을 수정 혹은 삭제시 서버의 다운이나 데이터베이스의 장애가 발생하는 것을 방지하고자 만들어졌다. 장애가 발생시 이전 상황으로 돌아가는 rollback 역할을 한다. 단순 조회는 데이터가 변하지 않기 때문에 트래잭션이 발생하지 않아 어노테이션을 명시하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 CRUD가 Service에서 어떠한 방식으로 처리 되는지 알아보자&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;C(create)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 registerComment가 Post역할을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;들어가기 앞서 Repository 파일의 역할을 알아보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Repository 파일은 실제 데이터베이스와 연결된 액세스 계층이다. Repository의 메서드를 사용하여 따로 일일이 SQL쿼리문 작성 없이 CRUD작업을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Domain 파일의 객체를 생성한 후 commentRepository.save(comment) save메서드를 통해 객체를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;R(Read) &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Get(조회) 현재 두가지있다. getAllComments와 getCommentById 전자는 모든 정보를 불러들이며 후자는 내가 조회하고자 하는 id의 값을 통해 해당 id가 있는 정보를 조회할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;findAll()메서드를 통해 모든 정보를 불러들이며 findById는 해당 id값의 정보를 불러들인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;U(update)&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Put, Patch) 둘다 조회인데 차이점은 목적에 따라 다르다 Put은 수정에 있어 하나만 수정시 다른 값이 null 이 되며 Patch는 다른 값을 건들지 않고 원하는 데이터만 수정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 여기서 Patch를 사용할 것이며 updateComment로 구현한다. Patch는 ㄴ수정을 원하는 id만을 불러들여 수정하며 내가 검색한 id와 정보가 들어있는 Dto를 불러들이며 정보를 수정할 데이터를 findByid()메서드를 통해 찾는다 이후 그 객체를 commentOptional 변수에 저장하여 수정이 완료되면 save()메서드를 통해 Repository에 저장하고 해당 원하는 id가 존재하지 않을 시 &quot;Comment is not found id&quot;를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;D(Delete)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delete(삭제)는 내가 삭제하고자 하는 id값만 받은 후 deleteById()메서드를 통해 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 지금까지 Repository의 메서드를 사용하여 CRUD의 작동원리를 익혔다. 다음으로는 클라이언트의 요청을 받아 서비스 계층에 위임하며 사용자에게 interface를 표시해주는 HTTP 상태코드를 생성하는 Controller파일을 분석하려고한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Comment_Controller.png&quot; data-origin-width=&quot;2037&quot; data-origin-height=&quot;3946&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZX0m2/btsHBPQ73pu/VPLmSxZGBMI4bIiZU8WVn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZX0m2/btsHBPQ73pu/VPLmSxZGBMI4bIiZU8WVn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZX0m2/btsHBPQ73pu/VPLmSxZGBMI4bIiZU8WVn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZX0m2%2FbtsHBPQ73pu%2FVPLmSxZGBMI4bIiZU8WVn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2037&quot; height=&quot;3946&quot; data-filename=&quot;Comment_Controller.png&quot; data-origin-width=&quot;2037&quot; data-origin-height=&quot;3946&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RestController: @Controller + @RespnseBody로 목적은 데이터를 JSON의 형태로 객체를 반환하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@RequestMapping: 우리는 url(&quot;/api/comments)로 요청을 보내면 Controller에서 어떠한 방식으로 처리할지 정의하는데 이떄 들어온 요청을 특정 메서드와 매핑하기 위해 사용하는 어노테이션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@---Mapping: 은 원하는 요청에 대한 각 매핑이 이루어지는 메서드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service에서 생성한 데이터 처리를 통해 ResponseEntity 메서드를 통해 응답코드를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ok(HTTP 상태 코드: 200) 성공적으로 처리되며 본문이 담겨 있다는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;noContent(HTTP 상태 코드: 204) 로 Delete에서 사용되는데 삭제되었으므로 본문이 없어야한다. 따라서 204가 뜬다는 것은 처리가 되었다느 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;notFound(HTTP 상태 코드: 404)로 클라이언트가 요청한 데이터가 없다는 의미다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;------결론------&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CommentController 파일의 전체적인 코드 형태를 보면 각 요청에 대한 메서드가 생성 되어있고 dto를 통해 데이터를 기존에 만든 Service(데이터 처리 담당)에서 각 요청에 받는 데이터를 처리한 후 거기에 맞는 응답코드를 생성하는 역할까지 하는 것을 볼 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 지금까지 Comment(댓글)에 관련된 CRUD의 작동원리와 클라이언트에서 서버까지 어떠한 방식으로 처리되는지 살펴보았다.&lt;/p&gt;</description>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/2</guid>
      <comments>https://endgame1019.tistory.com/2#entry2comment</comments>
      <pubDate>Mon, 27 May 2024 11:39:43 +0900</pubDate>
    </item>
    <item>
      <title>Vscode java package 경로 설정</title>
      <link>https://endgame1019.tistory.com/1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Vscode에서 java 파일에 Package를 사용하려고 할 떄&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Error: Could not find or load main class &amp;lt;fileName&amp;gt;&lt;br /&gt;Caused by: java.lang.NoClassDefFoundError: &amp;lt;fileName&amp;gt; (wrong name: &amp;lt;packageName&amp;gt;/&amp;lt;fileName&amp;gt;) 이 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 필자의 Setting.json에는&lt;/p&gt;
&lt;div style=&quot;background-color: #212121; color: #eeffff;&quot;&gt;
&lt;div&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #ffcb6b;&quot;&gt;java&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #eeffff;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;cd $dir &amp;amp;&amp;amp; javac $fileName &amp;amp;&amp;amp; java $fileNameWithoutExt&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 설정되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 패키지이름 = com.test&amp;nbsp; , 파일 = Hello.java 인 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력창 즉 터미널에서 아래와 같이 작성하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #c3e88d;&quot;&gt;cd &quot;$dir&quot; &amp;amp;&amp;amp; javac Hello.java &amp;amp;&amp;amp; java com.test.Hello&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;,&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #89ddff;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결이 됩니다.&lt;/p&gt;</description>
      <category>자바 #패키지 #컴파일에러 #개발 #개발일지 #코딩 #모각코 #네카라쿠배 #야당토 #처음</category>
      <author>컴퓨터와 하나가 된다.</author>
      <guid isPermaLink="true">https://endgame1019.tistory.com/1</guid>
      <comments>https://endgame1019.tistory.com/1#entry1comment</comments>
      <pubDate>Thu, 2 May 2024 21:17:43 +0900</pubDate>
    </item>
  </channel>
</rss>