wwj

@z9mb1@hackers.pub · 5 following · 13 followers

SANE PROGRAMMER

3년차 웹 프런트엔드 개발자입니다. 잠시 10주 여름 방학 동안 계약직으로 일할 수 있는 직장을 찾고 있습니다. (6월 마지막 주부터 8월 마지막 주) http://frontend.moe/portfolio/

올해 2학기까지 수료하면 졸업 예정이라, 학부 졸업 이후 정규직 전환 조건으로도 희망하고 있습니다.

4

터미널에 대해 궁금한 점이 생겨 자료를 찾다 보니 Windows Console Team에서 연재한 시리즈물을 발견했는데, 그 내용이 참 유익했다.

Windows Command-Line Series:

  1. Backgrounder
  2. The Evolution of the Windows Command-Line
  3. Inside the Windows Console
  4. Introducing the Windows Pseudo Console (ConPTY)
  5. Unicode and UTF-8 Output Text Buffer

콘솔 앱이 터미널과 입출력을 주고 받는 것을 공기처럼 당연하다고 생각했는데, 그 과정에는 커서 이동이나 개행 등과 같은 제어 문자를 렌더링하거나 SIGINT 같은 시그널을 발생시켜주는 처리가 존재했다. 터미널과 콘솔 앱이 서로 분리된 구조가 과거 물리 터미널로부터 비롯된 것도 흥미로웠고 말이다. 사실 이해 못 한 부분이 아직 많아서 다음에 또 읽어볼 생각이다.

6
0
0

Uv 압도적으로 빠르면 pip 그냥 없애버리고 싶다 도커 빌드에서 300초 걸릴 때 스트레스 받음.. 더 나은 방법 있나?? 찾을 힘도 없다..

1
1

Show HN: GitHub 코드베이스를 쉬운 튜토리얼로 변환하는 AI
------------------------------
- AI를 활용하여 GitHub 코드베이스를 초보자 친화적인 튜토리얼로 변환하는 프로젝트인 Pocket Flow
- GitHub 저장소를 크롤링하여 코드의 핵심 추상화를 분석하고 시각화를 통해 복잡한 코드를 쉽게 이해할 수 있는 튜토리얼로 변환함
- AI가 자동으로 생성한 다양한 GitHub 저장소의 예시 결과 제공
- 프로젝트…
------------------------------
https://news.hada.io/topic?id=20436&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

0
1
0
0
0
0
0
2
0
0
0

학교 캡스톤에서 절반 넘는 팀이 장고를 사용하는 신세카이를 보셨나요? 나도 멋진거 만들고 싶은데 현실적인거 고려하니까 걍 crud 웹앱이 되어버려서 눈물 난다

0
0
0

wwj shared the below article:

연합우주(fediverse)와 ActivityPub 프로토콜 이해하기: 개발자를 위한 가이드

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

연합우주란 무엇일까?

X(구 Twitter)나 Instagram 같은 중앙화된 소셜 미디어에 지치셨나요? 데이터 프라이버시, 알고리즘 추천, 그리고 끊임없는 광고가 걱정되시나요? 여기 대안이 있습니다. 바로 연합우주(fediverse)입니다.

페디버스(fediverse)는 “federated”(연합된)와 “universe”(우주)를 합친 말로, 한국어권에서는 주로 “연합우주”라고 불립니다. 연합우주는 하나의 거대한 플랫폼이 아닌, 서로 대화할 수 있는 독립적인 서버(인스턴스)들의 네트워크입니다.

이게 어떻게 가능할까요? 바로 ActivityPub이라는 프로토콜 덕분입니다. 이 프로토콜은 서로 다른 소셜 미디어 플랫폼이 정보를 교환할 수 있게 해주는 공통 언어 같은 것입니다.

연합우주는 어떻게 작동하나요?

연합우주를 이해하는 가장 쉬운 방법은 이메일 시스템과 비교하는 것입니다.

Gmail 사용자가 네이버 메일 사용자에게 이메일을 보낼 수 있는 것처럼, Mastodon 사용자는 Misskey나 PeerTube 사용자와 소통할 수 있습니다. (Mastodon, Misskey, PeerTube가 무엇인지는 아래에서 설명하겠습니다. Gmail과 네이버처럼 서로 다른 서비스라고 보시면 됩니다.) 이것이 가능한 이유는 이 서비스들이 모두 같은 언어인 ActivityPub 프로토콜로 대화하기 때문입니다.

연합우주에서 사용자 ID는 @사용자명@인스턴스.도메인 형식으로 되어 있습니다. 이메일 주소와 매우 비슷하죠? 예를 들면:

  • @honggildong@mastodon.social: mastodon.social 인스턴스 사용자
  • @kimcheolsu@pixelfed.social: pixelfed.social 인스턴스 사용자
  • @leeyeonghui@misskey.io: misskey.io 인스턴스 사용자

연합우주의 다양한 플랫폼 둘러보기

연합우주는 마치 여러 행성으로 이루어진 태양계 같습니다. 각 행성(플랫폼)은 고유한 특성을 가지고 있지만, 모두 같은 우주(연합우주)에 속해 있죠. 아래 표에서 주요 플랫폼들을 살펴봅시다:

플랫폼 설명 주요 인스턴스 특징
Mastodon X(구 Twitter)와 유사한 마이크로블로깅 플랫폼 • mastodon.social (공식 인스턴스)
• 우리.인생 (한국 중심)
500자 제한의 짧은 게시물, 해시태그, 컨텐츠 경고 기능
Misskey 일본에서 개발된 고도로 커스터마이징 가능한 마이크로블로깅 플랫폼 • misskey.io (가장 인기 있는 일본 인스턴스)
• 스텔라 (한국 중심)
리액션, 게임, 채팅 등 다양한 기능, 높은 커스터마이징 가능성
Pixelfed Instagram과 유사한 이미지 공유 플랫폼 • pixelfed.social (공식 인스턴스)
• 추억:사진 (한국 중심)
스토리, 필터, 발견 기능
PeerTube YouTube와 유사한 비디오 호스팅 플랫폼 • PeerTube.TV P2P 기술로 비디오 스트리밍, 채널, 재생목록
WriteFreely 미니멀한 블로그 플랫폼 • write.as Markdown 지원, 심플한 디자인
Lemmy Reddit과 유사한 링크 애그리게이터 및 토론 플랫폼 • lemmy.ml
• YuruLemmy (한국 중심)
커뮤니티(서브레딧과 유사), 투표, 토론

플랫폼 vs 인스턴스: 무슨 차이가 있을까?

연합우주를 이해할 때 흔히 혼동되는 개념이 있습니다. 바로 플랫폼(소프트웨어)과 인스턴스(서버)의 차이인데요.

플랫폼은 Mastodon, Misskey, Pixelfed와 같은 소프트웨어 자체를 의미합니다. 이들은 오픈 소스 소프트웨어로, 누구나 다운로드받아 설치할 수 있습니다.

인스턴스는 그 소프트웨어를 실행하는 개별 서버를 말합니다. mastodon.social과 우리.인생은 모두 Mastodon 플랫폼을 실행하는 별도의 인스턴스입니다.

Meta의 Threads 같은 일부 서비스는 플랫폼과 인스턴스가 동일합니다. 하지만 대부분의 연합우주 서비스는 여러 인스턴스로 구성되어 있습니다.

연합우주의 매력 포인트

연합우주가 갖는 몇 가지 매력적인 특징이 있습니다:

  1. 탈중앙화: 특정 기업이 모든 데이터와 규칙을 통제하지 않습니다. 각 인스턴스는 자체 규칙을 가질 수 있습니다.
  2. 데이터 주권: 자신의 데이터에 대한 더 많은 통제권을 가질 수 있습니다.
  3. 검열 저항성: 한 인스턴스가 차단되더라도 다른 인스턴스로 쉽게 이동할 수 있습니다.
  4. 커뮤니티 중심: 각 인스턴스는 특정 관심사나 지역 커뮤니티를 중심으로 형성됩니다.
  5. 다양성: 다양한 플랫폼과 인스턴스가 존재하여 선택의 폭이 넓습니다.

연합우주 시작하기

연합우주에 참여하는 것은 생각보다 쉽습니다:

  1. 자신의 관심사나 지역과 관련된 인스턴스를 선택합니다.
  2. 해당 인스턴스에 계정을 만듭니다.
  3. 다른 인스턴스의 사용자들을 팔로우하고 소통을 시작합니다!

한국 사용자라면 Mastodon 인스턴스인 우리.인생, Misskey 인스턴스인 스텔라 같은 한국어 중심 인스턴스를 추천합니다. 한국어 환경을 지원하고 한국 사용자들이 활발하게 활동하고 있어 시작하기 좋습니다.

아니면 이 글이 올라온 Hackers' Pub도 괜찮습니다. 소프트웨어 엔지니어들을 위한 소셜 미디어랍니다. 아직 개발중이라 공개적으로 가입을 받고 있지는 않습니다만, 홍민희에게 연락 주시면 계정을 생성해 드릴 수 있습니다.

ActivityPub: 연합우주의 심장

이제 개발자 관점에서 ActivityPub이 어떻게 작동하는지 자세히 살펴보겠습니다.

ActivityPub은 W3C에서 권장하는 표준 프로토콜로, 분산 소셜 네트워킹의 기반이 됩니다. ActivityStreams 2.0 데이터 형식을 기반으로 하며, 서로 다른 서버 간에 정보를 교환하는 방법을 정의합니다.

ActivityPub의 핵심 개념

ActivityPub은 몇 가지 핵심 개념으로 구성됩니다:

  1. 액터(actor): 사용자, 그룹 등 행동을 수행할 수 있는 주체입니다. 각 액터는 고유한 URL을 가지며, 수신함(inbox)과 발신함(outbox)을 가집니다.
  2. 액티비티(activity): 액터가 수행하는 행동으로, 게시물 작성, 댓글 좋아요, 다른 사용자 팔로우 등이 있습니다.
  3. 객체(object): 텍스트 게시물, 이미지, 비디오와 같이 생성되고 공유되는 콘텐츠입니다.

실제 작동 방식

홍길동(@honggildong@mastodon.social)이 게시물을 작성하고, 이영희(@leeyeonghui@misskey.io)가 이에 반응하는 과정을 살펴봅시다:

  1. 게시물 작성: 홍길동이 Mastodon에서 게시물을 작성합니다. Mastodon 서버는 이 게시물을 ActivityStreams 2.0 형식의 Create(Note) 액티비티로 변환합니다. 이 액티비티는 홍길동의 팔로워(이영희 포함)에게 전달됩니다.

  2. 게시물 수신: 이영희의 Misskey 서버는 이 액티비티를 받고 처리하여 이영희의 타임라인에 홍길동의 게시물을 표시합니다.

  3. 상호작용: 이영희가 게시물에 좋아요를 누르면, Misskey 서버는 Like(Note) 액티비티를 생성하여 홍길동의 Mastodon 서버로 보냅니다. 홍길동은 이영희가 자신의 게시물을 좋아했다는 알림을 받게 됩니다.

마치 다른 언어를 사용하는 사람들이 통역사를 통해 대화하는 것과 비슷하죠? ActivityPub이 바로 그 통역사 역할을 합니다.

ActivityPub의 실제 메시지 들여다보기

개발자로서 실제 ActivityPub 메시지가 어떻게 생겼는지 궁금하실 텐데요. 몇 가지 예시를 살펴봅시다:

1. 사용자 프로필(액터) 정보

{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1"
  ],
  "id": "https://mastodon.social/users/honggildong",
  "type": "Person",
  "preferredUsername": "honggildong",
  "name": "홍길동",
  "summary": "연합우주의 개척자",
  "inbox": "https://mastodon.social/users/honggildong/inbox",
  "outbox": "https://mastodon.social/users/honggildong/outbox",
  "followers": "https://mastodon.social/users/honggildong/followers",
  "following": "https://mastodon.social/users/honggildong/following",
  "publicKey": {
    "id": "https://mastodon.social/users/honggildong#main-key",
    "owner": "https://mastodon.social/users/honggildong",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
  },
  "icon": {
    "type": "Image",
    "mediaType": "image/jpeg",
    "url": "https://mastodon.social/system/accounts/avatars/000/000/001/original/avatar.jpg"
  }
}

이 JSON 데이터는 홍길동의 프로필 정보를 담고 있습니다. 사용자 이름, 소개, 프로필 사진 URL, 그리고 중요한 inboxoutbox URL이 포함되어 있죠.

2. 게시물 작성 액티비티

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://mastodon.social/users/honggildong/statuses/123456/activity",
  "type": "Create",
  "actor": "https://mastodon.social/users/honggildong",
  "published": "2025-02-21T14:30:00Z",
  "to": [
    "https://www.w3.org/ns/activitystreams#Public"
  ],
  "cc": [
    "https://mastodon.social/users/honggildong/followers"
  ],
  "object": {
    "id": "https://mastodon.social/users/honggildong/statuses/123456",
    "type": "Note",
    "content": "<p>연합우주에 오신 것을 환영합니다! #fediverse #연합우주</p>",
    "published": "2025-02-21T14:30:00Z",
    "attributedTo": "https://mastodon.social/users/honggildong",
    "to": [
      "https://www.w3.org/ns/activitystreams#Public"
    ],
    "cc": [
      "https://mastodon.social/users/honggildong/followers"
    ],
    "tag": [
      {
        "type": "Hashtag",
        "href": "https://mastodon.social/tags/fediverse",
        "name": "#fediverse"
      },
      {
        "type": "Hashtag",
        "href": "https://mastodon.social/tags/연합우주",
        "name": "#연합우주"
      }
    ]
  }
}

이것은 홍길동이 게시물을 작성했을 때 생성되는 Create(Note) 액티비티입니다. 게시물 내용, 해시태그, 공개 범위 등이 포함되어 있습니다.

3. 팔로우 액티비티

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://misskey.io/users/leeyeonghui/follow/1234",
  "type": "Follow",
  "actor": "https://misskey.io/users/leeyeonghui",
  "object": "https://mastodon.social/users/honggildong"
}

이영희가 홍길동을 팔로우할 때 생성되는 Follow 액티비티입니다. 단순하죠?

ActivityPub 서버 구현하기: 개발자를 위한 팁

직접 ActivityPub 서버를 구현하고 싶다면 다음 단계를 따라야 합니다:

  1. 액터 구현: 사용자 프로필 정보를 ActivityStreams 형식으로 제공합니다.
  2. 수신함과 발신함 설정: HTTP 엔드포인트를 만들어 액티비티를 받고 전송합니다.
  3. 서명 및 인증: HTTP Signatures를 사용하여 요청을 서명하고 검증합니다.
  4. 액티비티 처리: 다양한 액티비티 유형(Create, Follow, Like 등)을 처리하는 로직을 구현합니다.
  5. 데이터 저장: 사용자, 게시물, 액티비티 등의 정보를 데이터베이스에 저장합니다.
  6. 연합 정책 구현: 어떤 인스턴스와 연합할지, 어떤 컨텐츠를 허용할지 등을 설정합니다.

개발을 시작하기 전에 Mastodon, Misskey 같은 기존 구현체의 코드를 살펴보는 것이 도움이 됩니다. 처음부터 모든 것을 구현하는 것보다 Fedify 같은 프레임워크를 활용하는 것도 좋은 방법입니다.

WebFinger: 사용자를 찾는 방법

연합우주에서 @leeyeonghui@misskey.io 같은 사용자 ID를 어떻게 실제 ActivityPub 액터 URL로 변환할까요? 그 비밀은 WebFinger 프로토콜에 있습니다:

GET https://misskey.io/.well-known/webfinger?resource=acct:leeyeonghui@misskey.io

이 요청을 보내면 서버는 다음과 같은 응답을 반환합니다:

{
  "subject": "acct:leeyeonghui@misskey.io",
  "links": [
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://misskey.io/users/leeyeonghui"
    }
  ]
}

이제 https://misskey.io/users/leeyeonghui URL을 통해 사용자의 전체 프로필 정보를 얻을 수 있습니다. 마치 전화번호부에서 이름으로 전화번호를 찾는 것과 비슷하죠!

연합우주의 도전 과제와 미래

연합우주는 계속 성장하고 있지만, 몇 가지 도전 과제도 있습니다:

  1. 확장성: 수많은 서버 간의 통신을 효율적으로 처리하는 것은 쉽지 않습니다.
  2. 모더레이션: 각 인스턴스가 자체 규칙을 가지므로 콘텐츠 조정에 일관성이 부족할 수 있습니다.
  3. 발견성: 중앙화된 플랫폼에 비해 새로운 사용자나 콘텐츠를 찾기 어려울 수 있습니다.
  4. 사용자 경험: 일부 플랫폼은 아직 UI/UX 측면에서 개선이 필요합니다.

그러나 Threads와 같은 주요 서비스들이 ActivityPub을 채택하기 시작하면서, 연합우주의 미래는 밝아 보입니다. 개발자로서, 이런 성장하는 생태계에 참여할 수 있는 기회가 많이 있습니다.

마무리

연합우주와 ActivityPub은 중앙화된 소셜 미디어의 대안으로서 점점 더 주목받고 있습니다. 사용자에게 더 많은 통제권을 부여하고, 다양하고 풍부한 온라인 경험을 제공하는 연합우주의 세계는 계속해서 확장되고 있습니다.

개발자로서, 여러분은 이 새로운 탈중앙화된 웹의 생태계에 기여할 수 있습니다. 기존 애플리케이션에 ActivityPub 지원을 추가하거나, 완전히 새로운 서비스를 만들거나, 현재의 도전 과제를 해결하는 솔루션을 개발할 수 있습니다.

한국 개발자들의 참여가 늘어나면 한국 사용자들을 위한 더 다양하고 풍부한 서비스가 생길 것이고, 이는 더 건강하고 다양한 인터넷 문화를 만드는 데 기여할 것입니다.

그럼, 연합우주로의 여행을 시작해 보시는 건 어떨까요?

Read more →
1

딱히 천재도 아니고 PS도 가볍게만 즐기고 언어 덕후도 아닌 것 같아서 넓고 얕은 지식만 늘어나고 있다… 경력 1년 넘기 전에 좋아하는 분야 찾고 싶다 지금은 프로그램 설계하는게 좀 재밌는듯 (이것도 언제 식을지 모르지만)

0
0

wwj shared the below article:

애플리케이션 개발 측면에서 본 Drizzle ORM 대 Kysely 비교

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

TypeScript로 백엔드 서버를 개발하면서 적절한 ORM 선택은 항상 중요한 결정 중 하나입니다. 최근 제 프로젝트에서 Drizzle ORM과 Kysely를 모두 사용해 볼 기회가 있었는데, 개인적으로는 Drizzle ORM이 더 편리하고 생산성이 높았던 경험을 공유하고자 합니다.

두 ORM에 대한 간략한 소개

Drizzle ORM은 TypeScript용 ORM으로, 타입 안전성과 직관적인 API를 강점으로 내세우고 있습니다. 스키마 정의부터 마이그레이션, 쿼리 빌더까지 풀스택 개발 경험을 제공합니다.

Kysely는 “타입 안전한 SQL 쿼리 빌더”로 자신을 소개하며, 타입스크립트의 타입 시스템을 활용해 쿼리 작성 시 타입 안전성을 보장합니다.

두 도구 모두 훌륭하지만, 제 개발 경험에 비추어 볼 때 Drizzle ORM이 몇 가지 측면에서 더 편리했습니다.

Drizzle ORM을 선호하게 된 이유

스키마 정의의 직관성

Drizzle ORM의 스키마 정의 방식은 매우 직관적이고 선언적입니다:

import { pgTable, serial, text, integer } from 'drizzle-orm/pg-core';

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: text('name').notNull(),
  email: text('email').unique().notNull(),
  age: integer('age')
});

Drizzle ORM은 이 스키마 정의로부터 자동으로 CREATE TABLE SQL을 생성할 수 있어, 스키마와 코드가 항상 동기화되어 있습니다.

반면 Kysely는 타입 정의에 더 중점을 두고 있어 스키마와 타입 정의가 분리되는 경향이 있습니다:

interface Database {
  users: {
    id: Generated<number>;
    name: string;
    email: string;
    age: number | null;
  };
}

이 타입 정의는 TypeScript 코드에서 타입 안전성을 제공하지만, 이 타입 정의만으로는 CREATE TABLE SQL을 생성할 수 없다는 것이 결정적인 단점입니다. 실제로 테이블을 생성하려면 별도의 SQL 스크립트나 마이그레이션 코드를 작성해야 합니다. 이는 타입과 실제 데이터베이스 스키마 간의 불일치 가능성을 높입니다.

Drizzle의 접근 방식이 데이터베이스 스키마와 TypeScript 타입을 더 긴밀하게 연결해주어 개발 과정에서 혼란을 줄여주었습니다.

마이그레이션 경험

Drizzle ORM의 마이그레이션 도구(drizzle-kit)는 정말 인상적이었습니다. 스키마 변경사항을 자동으로 감지하고 SQL 마이그레이션 파일을 생성해주는 기능이 개발 워크플로우를 크게 개선했습니다:

npx drizzle-kit generate:pg

이 명령어 하나로 스키마 변경사항에 대한 마이그레이션 파일이 생성되며, 이를 검토하고 적용하는 과정이 매우 간단했습니다.

반면 Kysely의 마이그레이션은 본질적으로 수동적입니다. 개발자가 직접 마이그레이션 파일을 작성해야 하며, 스키마 변경사항을 자동으로 감지하거나 SQL을 생성해주는 기능이 없습니다:

// Kysely의 마이그레이션 예시
async function up(db: Kysely<any>): Promise<void> {
  await db.schema
    .createTable('users')
    .addColumn('id', 'serial', (col) => col.primaryKey())
    .addColumn('name', 'text', (col) => col.notNull())
    .addColumn('email', 'text', (col) => col.unique().notNull())
    .addColumn('age', 'integer')
    .execute();
}

async function down(db: Kysely<any>): Promise<void> {
  await db.schema.dropTable('users').execute();
}

이러한 수동 방식은 복잡한 스키마 변경에서 실수할 가능성이 높아지고, 특히 큰 프로젝트에서는 작업량이 상당히 증가할 수 있었습니다.

하지만 Kysely의 마이그레이션에도 두 가지 중요한 장점이 있습니다:

  1. TypeScript 기반 마이그레이션: Kysely의 마이그레이션 스크립트는 TypeScript로 작성되기 때문에, 마이그레이션 로직에 애플리케이션 로직을 통합할 수 있습니다. 예를 들어, S3와 같은 오브젝트 스토리지의 데이터도 함께 마이그레이트하는 복잡한 시나리오를 구현할 수 있습니다. 반면 Drizzle ORM은 SQL 기반 마이그레이션이므로 이러한 통합이 불가능합니다.

  2. 양방향 마이그레이션: Kysely는 updown 함수를 모두 정의하여 업그레이드와 다운그레이드를 모두 지원합니다. 이는 특히 팀 협업 환경에서 중요한데, 다른 개발자의 변경사항과 충돌이 발생할 경우 롤백이 필요할 수 있기 때문입니다. Drizzle ORM은 현재 업그레이드만 지원하며, 다운그레이드 기능이 없어 협업 시 불편할 수 있습니다.

참고로, Python 생태계의 SQLAlchemy 마이그레이션 도구인 Alembic은 훨씬 더 발전된 형태의 마이그레이션을 제공합니다. Alembic은 비선형적인 마이그레이션 경로(브랜치포인트 생성 가능)를 지원하여 복잡한 팀 개발 환경에서도 유연하게 대응할 수 있습니다. 이상적으로는 JavaScript/TypeScript 생태계의 ORM도 이러한 수준의 마이그레이션 도구를 제공하는 것이 바람직합니다.

관계 설정의 용이성

Drizzle ORM에서 테이블 간 관계 설정이 매우 직관적이었습니다:

import { relations } from 'drizzle-orm';

export const usersRelations = relations(users, ({ one, many }) => ({
  profile: one(profiles, {
    fields: [users.id],
    references: [profiles.userId],
  }),
  posts: many(posts)
}));

이 방식은 데이터베이스 설계의 본질적인, 관계적인 측면을 명확하게 표현해주었습니다.

쿼리 작성의 편의성과 동일 이름 칼럼 문제 처리

두 ORM 모두 쿼리 작성을 위한 API를 제공하지만, Drizzle의 접근 방식이 더 직관적이고 관계형 모델을 활용하기 쉬웠습니다:

// Drizzle ORM - db.query 방식으로 관계 활용
const result = await db.query.posts.findMany({
  where: eq(posts.published, true),
  with: {
    user: true  // 게시물 작성자 정보를 함께 조회  
  }
});

// 결과 접근이 직관적이고 타입 안전함
console.log(result[0].title);       // 게시물 제목
console.log(result[0].user.name);   // 작성자 이름 - 객체 구조로 명확하게 구분됨  
console.log(result[0].user.id);     // 작성자 ID - 게시물 ID와 이름이 같아도 문제 없음  

// Kysely
const result = await db
  .selectFrom('posts')
  .where('posts.published', '=', true)
  .leftJoin('users', 'posts.userId', 'users.id')
  .selectAll();

// 결과 접근 시 칼럼 이름 충돌 문제
console.log(result[0].id) // 오류: posts.id와 users.id 중 어떤 것인지 모호함  
console.log(result[0].name) // 오류: 둘 다 name 칼럼이 있다면 모호함  

Drizzle의 접근 방식이 테이블과 컬럼을 참조할 때 타입 안전성을 더 강력하게 보장하고, 관계를 활용한 쿼리 작성이 더 직관적이었습니다.

특히 여러 테이블 조인 시 동일한 이름의 칼럼 처리 부분에서 Drizzle ORM이 훨씬 더 편리했습니다. 이는 제 개발 경험에서 가장 중요한 차이점 중 하나였습니다.

// Drizzle ORM - 동일 이름 칼럼 처리
const result = await db.query.posts.findMany({
  with: {
    user: true  // posts.id와 users.id가 모두 있지만 자동으로 구분됨
  }
});

// 결과에 자연스럽게 접근 가능  
console.log(result[0].id);        // 게시물 ID  
console.log(result[0].user.id);   // 사용자 ID - 명확하게 구분됨  
console.log(result[0].user.name); // 사용자 이름  

// Kysely - 동일 이름 칼럼 처리를 위해 별칭 필요
const result = await db
  .selectFrom('posts')
  .leftJoin('users', 'posts.userId', 'users.id')
  .select([
    'posts.id as postId',       // 별칭 필수  
    'posts.title',
    'posts.content',
    'users.id as userId',       // 별칭 필수  
    'users.name as userName',   // 칼럼 이름이 같을 수 있으므로 별칭 필수  
    'users.email as userEmail'  // 일관성을 위해 모든 사용자 관련 칼럼에 접두어 필요  
  ]);

// 별칭을 통한 접근
console.log(result[0].postId);    // 게시물 ID
console.log(result[0].userId);    // 사용자 ID
console.log(result[0].userName);  // 사용자 이름

Drizzle ORM은 테이블과 칼럼을 객체로 참조하기 때문에, 동일한 이름의 칼럼이 있어도 자연스럽게 계층 구조로 처리되며 타입 추론도 정확하게 작동합니다. 반면 Kysely에서는 문자열 기반 접근 방식 때문에 별칭을 수동으로 지정해야 하는 경우가 많았고, 복잡한 조인에서 이런 작업이 번거로워졌습니다. 특히 여러 테이블에 같은 이름의 칼럼이 많을수록 모든 칼럼에 명시적인 별칭을 지정해야 하는 불편함이 있었습니다.

또한 Drizzle ORM은 결과 타입을 자동으로 정확하게 추론해주어 별도의 타입 지정 없이도 안전하게 결과를 사용할 수 있었습니다.

Kysely의 장점

물론 Kysely도 여러 강점이 있습니다:

  1. 더 가벼운 구조: 필요한 기능만 포함할 수 있는 모듈화된 구조
  2. SQL에 더 가까운 접근: SQL 구문에 매우 충실한 API 설계
  3. 유연성: 복잡한 쿼리에서 때로 더 유연한 작성이 가능

또한 앞서 언급했듯이, Kysely의 TypeScript 기반 마이그레이션과 양방향(up/down) 마이그레이션 지원은 특정 상황에서 Drizzle ORM보다 우위에 있는 기능입니다.

SQLAlchemy와의 비교 및 앞으로의 기대

JavaScript/TypeScript 생태계의 ORM을 이야기하기 전에, 여러 언어 중에서도 Python의 SQLAlchemy는 특별한 위치를 차지합니다. 개인적으로 여태 사용해본 다양한 언어의 ORM 중에서 SQLAlchemy가 가장 기능이 풍부하고 강력하다고 느꼈습니다. 복잡한 쿼리 구성, 고급 관계 매핑, 트랜잭션 관리, 이벤트 시스템 등 SQLAlchemy의 기능은 정말 방대합니다.

Drizzle ORM은 JavaScript 생태계에서 매우 인상적인 발전을 이루었지만, 아직 SQLAlchemy의 경지에는 이르지 못했다고 생각합니다. 특히 다음과 같은 부분에서 SQLAlchemy의 성숙도와 기능 풍부함이 돋보입니다:

  • 복잡한 서브쿼리와 윈도우 함수 지원
  • 다양한 이벤트 리스너와 훅
  • 다양한 상속 전략
  • 복잡한 트랜잭션 관리와 세션 관리
  • 대규모 프로젝트에서 검증된 안정성
  • Alembic을 통한 비선형적 마이그레이션 지원
  • 놀라울 정도로 방대하고 상세한 문서화

결론

두 ORM 모두 훌륭한 도구이지만, 제 개발 스타일과 프로젝트 요구사항에는 Drizzle ORM이 더 잘 맞았습니다. 특히 스키마 정의의 직관성, 강력한 마이그레이션 도구, 그리고 전반적인 개발자 경험 측면에서 Drizzle ORM이 더 생산적인 개발을 가능하게 해주었습니다.

동일 이름 칼럼 처리와 같은 실질적인 문제에서 Drizzle ORM의 객체 기반 접근 방식이 가져다주는 편리함은 실제 프로젝트에서 큰 차이를 만들었습니다.

ORM 선택은 결국 프로젝트 특성과 개인 선호도에 크게 좌우됩니다. 새로운 프로젝트를 시작한다면 두 도구 모두 간단히 테스트해보고 자신의 워크플로우에 더 적합한 것을 선택하는 것이 좋겠지만, 제 경우에는 Drizzle ORM이 명확한 승자였습니다.

앞으로 Drizzle ORM이 더욱 발전하여 SQLAlchemy 수준의 풍부한 기능과 유연성을 제공하게 되길 바랍니다. JavaScript/TypeScript 생태계에도 그런 수준의 강력한 ORM이 있으면 좋겠습니다. 다행히도 Drizzle ORM은 계속해서 발전하고 있으며, 그 발전 속도를 보면 기대가 큽니다.

여러분의 경험은 어떤가요? 다른 ORM 도구나 언어를 사용해보셨다면 의견을 공유해주세요!

Read more →
0
0
0

wwj replied to the below article:

2025 Q1 Review

Jaeyeol Lee @kodingwarrior@hackers.pub

작년 10월 쯤부터 강남에 파견근무를 가게 되었다. 간만에 돈벌이가 나쁘지 않은 생활, 요즘 받는거에 비하면 월급 두배 이벤트를 하고 있는데, 그만큼 너무 바빠졌다. 주말도 쉬지 않고 일했고, 설연휴도 삼일절 연휴도 쉬지도 못하고 일했다. 그러다 보니, 책을 읽을 시간도 없을 뿐더러, 사람을 만나러 다닐 여유도 거의 없다시피 했다. 일정을 잡는 것도 눈치봐야 하는 수준으로 바빠졌고, 이 일정이 언제 끝날지도 모르겟다.

그래서 블로그에 근황을 남기자니, "네.. 그냥 뺑이치고 있습니다..." 라고 밖에 요약이 되지 않는다.

요즘 근황이 어떻냐면....

블로그에 쓸만한 근황은 잘 없는 것 같지만, 그래도 몇가지 변경사항은 있는것 같아서 기록이라도 남겨야겠다. 대외활동을 하게 될 일은 당연히 없었어서 타임라인을 나열하기도 어렵고, "그냥 요즘 이런 변화가 생겼고, 이런 생각을 하고 있습니다" 정도로 남겨두겠다.

노트를 사서 끄적이는 습관을 들이려고 하는 중이다

삶에 변화를 좀 줘볼까하는 마음가짐에 프랭클린 플래너랑 속지를 구매했다. (사실 이런짓은 2016년/2020년 시도해본 적도 있었다) CEO 사이즈가 간편하기도 하고, 펜을 꽂을 수 있는 공간도 있어서 들고 다니면서 뭔가를 끄적이기에도 좋다.

Post by @kodingwarrior@silicon.moe
View on Mastodon
<script data-allowed-prefixes="https://social.silicon.moe/" async src="https://social.silicon.moe/embed.js"></script>

요즘은 일할때 아에 A4 용지 하나 꺼내서 거기다가 해야할 일들 나열하고, 어떤 Sub task를 해야하는지 시각적으로 쪼개기도 하는데, 키보드로 타이핑해서 할 일을 관리하는 것보다 역설적으로 더 관리가 잘 된다. 하나하나 남김없이 기록으로 남겨야겠다는 강박을 가지면 그것도 그것대로 집중이 안되었던 것 같다. 필요하면 그때그때 하나의 축약된 스냅샷을 남긴다면 모를까.

Getting Things Done 에 따르면, 할 일 관리 내지는 생산성의 끝판왕은 펜과 종이로 충분하다고도 설명하곤 했었는데, 왜 그런지는 요즘 들어서 실감하고 있다. 그렇다고, Vim을 사용하는 워크플로우가 별로이냐면 그것도 아니다. 각자, 담당할 수 있는 영역이 다를 뿐이고, 시각화가 필요하거나 시각적인 정보의 자유로운 배치를 원한다면 마우스로 어거지로 배치하느니 차라리 펜과 종이만한게 없다.

지하철 타고 다닐때나 버스를 타고 다닐때, 종이책을 들고 다니면서 읽거나 아이패드로 책을 읽곤 하지만, 책 자체가 내용이 많은건지 내 처리속도(1분당 1-2페이지)가 느린건지 유의미하게 읽는 양이 그렇게 많지는 않다. 꾸준히 읽는다는 것 자체에 의미를 둘 수는 있긴 하겠지만, '찔끔찔끔 읽으면서 내가 가져갈 수 있는게 무엇인가?'라는 실용적인 관점에서 접근해보니, 책 읽는데 시간을 들이기보다는 조금이라도 생각나는 것들을 다이어리에다가 기록이라도 남겨두면 이것들을 조합해서 밀린 계획들을 조금이라도 정리도 할 수 있고, 블로그에 글도 올리고, 블로그에 글을 올리겠다고 밀린 것들도 청산할 수 있고 일석이조 아닌가?

물론 책을 읽을 시간이 많으면 베스트겠다.

슬슬 취준을 시작하고 있다

지금 진행중인 3년이 넘는 계약도 슬슬 끝나간다. 취업 시장에 나올 수 있을때까지 한 6개월~1년 정도 남았다고 볼 수 있는데, 밥벌이를 하면서 취업 준비를 하기도 적당한 시기다. 사실은, "취업 준비"라는걸 제대로 해본 적도 없었다. 지금까지 해온 밥벌이도 그냥 코딩테스트는 그냥저냥 통과해서 그 운빨로 인턴을 시작하기도 했고, 그 다음부터는 지인(혹은 2차 지인)이 다니는 회사에 공식적인 전형이 없이 일을 해오긴 했었다. 그래서, 취업 준비를 하는 것도 이번이 처음이다.

여기에서도 간단하게 언급하긴 했었는데, 취준을 하게 된다면 프론트엔드 직군을 알아보거나 혹은 풀스택 직군을 알아보게 될 것 같다. 프론트엔드 직군을 생각하게 된 이유는 아래와 같다.

  • 돈이 되는 제품을 만드는건 결국 프론트에서 시작한다.

아무리 기능이 많더라도 사용성이 구리거나 이쁘지도 않다면, 그걸 쓰려고 하는 고객도 잘 없다. 그것은 즉슨 돈벌이가 되지도 않는다. 기능을 특정 고객에게 맞춤형으로 개발한다고 한들, 사용성이 구리거나 이쁘지도 않으면 다른 경쟁업체에게 빼앗기기 일쑤다. 돈이 되는 일을 하고 가치를 창출하려면 프론트엔드를 하는게 불가피하다는 결론에 도달했다.

  • 이왕 피할 수 없으면, 그냥 이대로 커리어로 끌고 가야겠다는 생각이 들었다.

본업은 분명히 백엔드로 시작하긴 했었지만, 실무에서 주로 하게 되었던 일들은 프론트엔드 할 사람이 없거나 혹은 일손이 모자라서 짬처리를 하는 일이었다. 거쳐갔던 회사 중에는 신중하게 기획하고 제품을 잘 만드는 것에 집중하고 기술스택을 가리지 않는 좋은 회사도 있었지만 이 경우는 짬처리와는 거리가 멀었다. 짬처리를 당하든, 내가 자발적으로 하게 되든, 결국에는 프론트엔드는 피할 수 없는 일이 되어왔다.

피할 수 없으면, 이걸로 계속 밥벌이를 하고 있으면, 그냥 이걸 내 커리어로 들고 가는게 맞지 않을까? 라는 생각이 들었다. 어차피, 백엔드도 그렇게 깊게 하지도 않았으니 프론트엔드가 손에 맞아가는 이 시점에 프론트엔드로 방향 트는 것도 방법이겠다 싶다.

프론트엔드 취준을 생각하면서도 걱정이 든다

프론트엔드 쪽으로 취업을 하려고 생각은 하고 있지만, 이래저래 걱정은 많다. 가장 먼저 드는 생각은, 내가 프론트엔드 개발을 할 때는 손이 그렇게 빠르지가 않다. Figma를 보면서 작업하면 금방이라고 느끼는 사람도 있겠지만, 하루에 10페이지-20페이지를 금방 찍어내는 사람이랑은 속도 차이가 좀 있는 것 같다.

거기다 처음부터 다시 배워야 하는 수준이다. 백엔드도 그렇게 깊게 하지는 않았지만, 프론트엔드는 더더욱 구조를 생각하면서 짜왔던 편도 아니거니와, 돌아만 가면 되는 수준으로 야매로 짜오긴 했다. 컴포넌트 나눠서 개발하는건 당연히 기본이긴 하지만, 잘 나누는지는 모르겠다. 그나마, "CSS는 과학이다"라는 마음가짐이었어서 CSS는 어느 정도 익숙하지만 딱 거기까지만인 것 같다.

지금까지 커리어를 이어오면서, 가장 취약했던 것도 사실은 프론트엔드이기도 하다. 퍼블리싱을 입히는 작업이 가장 괴롭게 느껴지기도 했었고, 다른 작업보다 심리적인 저항감이 있었어서 상대적으로 시간이 오래 걸리기도 했었다. (ADHD의 영향이 있어서일지도 모른다) 오히려 약점인 분야로 취업을 생각하고 있는 것도 어떻게 보면 이상하기도 하지만, "나는 프론트엔드 개발자다" 라는 마음가짐으로 임하게 된다면 그나마 저항감이 덜어질 것 같다.

당장은 할 수 있는 것부터 하고 있다

프론트엔드 개발자로서 어필하려면, 당장은 프론트엔드 개발자로서 포트폴리오가 될만한 것들을 만들어야 한다. 그러면서, 더더욱 의욕을 잃지 않을만한 것을 찾아서 만들어야 한다. 그래서 요즘은 나도 쓰고 남한테도 쓰라고 권장할 수 있는 앱을 만들려고 시도하는 중이다. 이 글을 쓰고 있는 Hackers Pub에 기여할 방법을 찾아보기도 하고, 직접 Mastodon 클라이언트를 만들고 있기도 하다. 다음 분기에는 꼭 출시하는게 목표다. 면접이나 과제 전형 준비는.... 일단 맞으면서 배워야겠지..

그래도 Full-stack 엔지니어(요즘 용어로는 Product 엔지니어) 라는 선택지도 완전히 버리지는 못해서 백엔드를 해야한다면 그때그때 습득하면 될 것 같다.

지금까지 읽은 책들

위에서 언급했다시피, 책 읽을 시간도 거의 확보하지 못했다. 집 - 사무실 - 집 - 사무실 루틴을 반복하는 것도 모자라서 최소 일주일에 한번 이상은 사무실에서 밤새기까지 해서 책을 읽을 정신적인 여력 조차도 없었다.

그나마 읽은 것들을 나열하자면....

  • 또라이 제로 조직 (No Asshole Rule)
    • 개인적으로 별로였다. 어떤 특징을 가진 사람을 또라이라고 규정하는 방식이나, 또라이라고 하는 사람이 조직에 얼마나 해로운지를 그럴듯한 설명을 하고 있지만, 이것도 주관적인 기준에 따라 다를 수 있기 때문에 평범한 사람도 또라이로 지목이 되어서 따돌림을 당하고도 남는 사회다.
    • 일부는 납득은 되지만, 어조가 너무 노골적인 책이었어서 개인적으론 별로였다. 노골적인게 누군가에겐 사이다일 순 있겠지만, PTSD 있는 사람들에겐 피하라고 하고 싶은 책이다.
  • RAG에 대한 책을 읽긴 했는데, 아직 공식적인 제목은 나오진 않았다. JPub에서 협찬을 받았지만, 출간 소식이 공식적으로 올라오면 그 때 링크를 달아두겠다.
  • 큐레이션 : 정보 과잉 시대에서 쓸모에 맞게 조합해서 전시하는 것만으로도 어떤 가치를 전달할 수 있는지 잘 설명해주는 책이다. 알고리즘 기반의 추천이 어떻게 보면 이 시대의 큐레이션이라고 볼 수 있지 않을까?
  • 노 필터(-ing) : 인스타그램 창업 스토리를 다루고 있는 책인데, 지금 읽고 있는 중이다. "사진을 찍고, 공유한다"라는 핵심적인 기능을 파고 들어서 시장에서 독보적인 위치를 차지해온 서사가 재밌다. 근데, 책 읽을 시간도 계속 없어져서 어느 시점부터는 맥락이 날아갈 것 같다.

And...?

이젠 좀 바쁜 것도 끝이 보이고, 이젠 진짜 하고 싶은거 많이 하면서 다음 분기를 보내고 싶다.

  • Vim 행사 열기
    • 좀 더 초보자들 친화적이고, 좀 더 많은 사람들에게 와닿고, 특히 Vim 자체에 어려움을 겪는 학생들에게도 Vim에 대해 가지는 "접하기 어렵다" 라는 고정관념을 타파할 수 있는 행사를 여는게 목표다.
    • 지난 주부터 서베이를 돌렸는데, 44명이나 되는 분들이 응해주셨다. 이미 큰 행사를 열 것으로 계획하고는 있었지만, 정말 큰 행사가 될 것 같다
  • JLPT N3 따기
    • 듀오링고 일본어 모든 섹션을 다 완주하고 나서 자신감이 생겼다. 한자를 공부하는게 좀 고역이긴 하겠지만, 쪼끔이라도 잠깐 훑어보면 되지 않을까?라는 나이브한 생각이긴 하다. 어차피, 일본으로 넘어가는게 목표이기도 하겠다, N3 따는 걸로 시작해서 그 다음은 N2, 그 다음은 N1 점진적으로 따려고 한다.
    • 일본 이민가기 프로젝트... 성공하겠지...?
  • 만들고 있는 Mastodon Client를 플레이스토어에 출시하기
Read more →
0
0
0

@z9mb1wwj 일단 진입 장벽이 있고, 그걸 넘어서고 나면 손에 익어서 다른 에디터 쓰는 게 불편해집니다. 그런데 요즘엔 다른 VS Code처럼 더 좋은 게 많거든요… 더 편한 에디터를 쓰고 싶어도 불편해서 갈아타기 어려워지는 거죠. 실제로 제가 그래서 VS Code에서 VSCodeVim이라는 확장을 쓰고 있어요. 그런데 굳이 이렇게까지 해야 할 만큼 Vim/Neovim이 요즘 세상에 특별한 가치를 지니는지 잘 모르겠어요.

0

Hello, I'm an open source software engineer in my late 30s living in , , and an avid advocate of and the .

I'm the creator of @fedifyFedify: an ActivityPub server framework, an server framework in , @holloHollo :hollo:, an ActivityPub-enabled microblogging software for single users, and @botkitBotKit by Fedify :botkit:, a simple ActivityPub bot framework.

I'm also very interested in East Asian languages (so-called ) and . Feel free to talk to me in , (), or (), or even in Literary Chinese (, )!

0
4
0

디지털 가드닝에 관심이 많은 개발자입니다.
- 특히 위키 형식의 문서 관리, knowledge graph 구조의 시각화에 관심이 있어요.
- kodingwarrior.github.io/wiki

Neovim 이라는 텍스트 에디터에 굉장히 꽂혀있습니다.
- 과몰입한 나머지 플러그인까지 개발해본 경험이 있어요.
- 한국어권 개발자를 위한 Vim 디스코드를 운영중입니다 (vim.kr)

프로그래밍을 하는 행위 자체를 좋아합니다.
- 프로그래밍으로 퍼즐을 푸는 행위를 좋아했고, 비슷한 흔적을 가진 사람들에게 친밀감을 느낍니다.

0
0
0

2025년에 누가 Vim/Neovim 입문한다고 하면 도시락 싸들고 다니며 말리고 싶은 마음. 나야 이미 너무 익숙해져서 벗어나기 힘들게 됐지만… 그만큼 벗어나기 힘든 습관을 가지면서까지 얻는 이득이 요즘 시대엔 크지 않다고 느낀다.

0

Pc로 슈단 돌리고 스팀 링크 이용해서 rtd p 3+로 플레이 중… /n 근데 버튼 커스텀하다가 우측 조이스틱이 고장 냈다. 그리고 샤오미 드라이버 셋트 😻 #ultimate #despair

0

Pc로 슈단 돌리고 스팀 링크 이용해서 rtd p 3+로 플레이 중… /n 근데 버튼 커스텀하다가 우측 조이스틱이 고장 냈다. 그리고 샤오미 드라이버 셋트 😻 #ultimate #despair

0
0
0
0
0
0
1
0

이틀은 출근하고 사흘은 학교 가는데 게임할 시간이 없어서 괴롭다 😱 단간론파 번들 샀는데 아직도 1편 2챕이다… 회사에서 쓰는거 공부도 해야하고 (아직 주니어라 자주 코드 지적질 받음) 학교에선 팀플 리딩 🫠🫠 게임할 자유를 보장하라

0

슬슬.... 계약도 끝이 보이고 해서 취업준비 모드로 들어가려고 하는데, 이미 꼬여버린 커리어니까 다시 새 출발한답시고 어떤 방향으로 진로를 잡을지 생각중이다. 당장은 웹개발 쪽으로 가는걸 지향하는데? 백/프론트 둘 다 해본 입장에서 백엔드로 갈지 프론트엔드로 갈지 애매한 입장이다. MLOps나 DevOps도 열려있긴 하다. 사실 다 찍먹해볼 수 있는 인공지능 잘 쓰는 회사에서 일하게 된다면 정말 좋을 것 같다.

취준 공부 전략은.... 당장 내가 써먹을 수 있는 방향으로 전이될 수 있는 방향으로 가는 것이 좋을 것 같다. 인공지능에 관심이 생기긴 했으니, 통계나 ML 관련 지식은 어느 정도 감잡을 정도로 곁다리로 공부는 할 것 같다. 메인 분야를 하나 잡고 준비하는게 베스트이긴 한데, 준비할 수 있는 시간은 많으니 메인 분야는 프론트엔드로 잡고 가지 않을까 싶다. 돈이 되는 개발을 하려면 프론트엔드로 가는게 맞더라는게 이리 구르고 저리 구르다가 내린 결론이다.

뭐부터 공부할지 우선순위 정하는 것도 역시 나한테 당장 도움이 되고 바로 써먹을 수 있는 것들 중심으로 가중치를 매겨야 할텐데, 소프트웨어 설계 전략, 추상화, 시스템 디자인, javascript 런타임, 웹 퍼포먼스 등등 주제는 다양하게 있고, roadmap.sh에서도 몇가지 키워드를 주워담고 있다. 지금 밥벌이하는 동안에는 프론트엔드가 중심인 풀스택의 관점에서 접근하게 될 것 같다. 풀스택이 힘든길이 되겠지만... 전반적인 사이클 한바퀴 돌리고 개발하는게 익숙하다.

개인 프로젝트를 만들면서 포폴을 만들어나갈 것 같은데, 밀어붙이고 싶은 스택은 Django/Vue/Flutter 정도? 사실은 어떤 스택이든 상관은 없다. 먹고사니즘을 위해서라면 뭐라도 해야지... 내가 좋아하는 것을 깊게 팔 수 있으면 베스트지만, 상황이 따라주지 않는다면 그냥 하던거나 깊게하면서 내가 좋아하는 기술에 지식이 전이될 수 있는 방향으로 깊게 파볼까 싶다.

0
0