bgl gwyng

@bgl@hackers.pub · 81 following · 91 followers

슈티를 함께 만들 팀을 만들고 있습니다. 관심 있으신 분, 또는 잘 모르겠지만 이야기를 나눠보고 싶은 분도 bgl@gwyng.com으로 편하게 연락주세요.

GitHub
@bglgwyng
shootee
www.shootee.io
0

@domatdo도막도 넵 그렇습니다. 제가 처음에 올린 단문도 객체지향이 설계에 실질적으로 도움이 되는 사례를 나열해보면 상속을 벗어나지 않는다는 주장이었습니다. 메시지 패싱으로 어떤 설계 문제를 해결했다,는 사례를 저는 아직 모르고, 최소한 잘 알려지지 않은거 같습니다.

1

@bglbgl gwyng 객체지향 프로그래밍의 잘못된 적용례로 단순 구조체에 불과한 객체를 양산하는 상태 주도 개발을 꼽는 것도 동일한 맥락에서입니다. 메시지 패싱 없는 객체지향 프로그래밍은 객체지향 프로그래밍이 아니어요.

1
0

@domatdo도막도 좋은 책 추천 감사합니다! 그런데 제가 의심하는게 사실 '객체와 그것들의 협력에 대한 이야기' 요 부분입니다. 가령 Java와 같은 객체지향 패러다임의 언어 프로그래밍 언어가 실제로 객체들의 협력을 위해 언어적으로 어떤 기능을 지원하나요? 저는 그런 기능이 딱히 없다고보고, 그래서 1. Java는 객체지향 언어가 아니다 2. 객체지향 패러다임은 (실질적으로) 그 문제와 별로 관련이 없다. 두 결론 중 하나를 택해야 한다고 생각합니다.

0

bgl gwyng shared the below article:

유머는 설명해야 하면 실패한 거다

정진명의 굳이 써서 남기는 생각 @index@guji.jjme.me

"유머는 그 유머가 왜 웃긴지를 설명해야 하는 순간 실패"라는 말이 있다. 나는 이 말을 별로 좋아하지 않는다. 일단 그 말이 틀렸다고 말하고 싶은 건 아니다. 유머를 설명해야 한다면, 많은 사람들이 그걸 바로 받아들이거나 동의하는 데 실패했다는 이야기이고, 대화의 흐름이 끊어지고, 유머를 발화함으로 달성하고자 했던 여러 목표들이 달성되지 않는 경우가 많은 것은 부정할 수 없기 때문이다.

하지만 피터의 법칙과 유사하게, 대부분의 유의미한 말은 그 말을 적용하는 것이 적절하지 않은 지점까지 재생산된다. 이 말의 경우 적절하지 않은 지점은 어디일까? 유머에도 피터의 법칙 같은 건 존재한다. 잘 작동하던 유머가 있으면, 그 유머는 퍼지게 되어 있다. 리트윗과 좋아요가 있고 밈 재생산에 적합한 매체에서는 특히 쉽다. 처음에는 잘 작동하는 유머 또한 피터의 법칙처럼 실패하는 지점까지 퍼질 수 있고, 그 지점에서 '설명'해야 하는 유머가 된다. 그 시점에서 누군가는 이런 말을 할 수 있다. "(나한테) 설명이 필요한 유머라니, 이것은 (객관적으로) 실패한 유머이다." 나는 이 지점이 싫다. 그냥 널리 퍼진 유머를 발신한 사람이 자연 현상처럼 받아들여야하는 일일지도 모르겠지만.

반대 방향의 이야기이지만, 나는 xkcd, SMBC가 왜 재미있는지, 일본어로 올라오는 수많은 팬아트가 왜 재미있는지를 알기 위해서 그 유머를 가능하게 하는 기반 지식을 찾아보는 편이다. 나만 그런 것은 아니다― 세상에는 explain xkcd같은 사이트도 있다. 나는 유머를 삶에서 분리해낼 수 없는 사람이다. 그런 나한테는 설명이 필요한 유머야말로, 내가 모르는 세상을 향해 열린 창이다.

Read more →
0
1

방금 하스켈 학교에서 객체지향 vs 함수형 떡밥이 n번째로 돌았는데, 나는 그냥 객체지향 = 상속(서브타이핑) 이라고 생각한다. 객체지향에서 상속을 빼면 뭐 남는게 없다. 그래서 객체지향이란 단어를 의미있게 사용하려면 상속이랑 동치시켜 사용할수 밖에 없다고 본다.

근데 상속은 코드를 합성하는 수많은 방법중 하나일 뿐이다. Java같은 언어는 그 수많은 방법중 딱 하나 상속만을 언어 자체에서 지원하는거고, 거기서 벗어나는 다른 유용한 추상화들은 죄다 디자인 패턴이라고 퉁쳐서 부른다. 그래서 객체지향 vs 함수형(= 상속 vs 함수형)은, 나에겐 Monoid vs 타입클래스 같은 비교처럼 들린다. 좌변과 우변이 체급이 안 맞아서 대결이 불성립한다.

3
1
7
0
0

여태까지 地下鐵(지하철)에서 다음 ()이 어딘지 알 수 없을 때마다 머릿속에서 온갖 陰謀論(음모논)을 떠올렸었는데… 이제라도 다음 ()常時(상시) 表示(표시)된다니 多幸(다행)이네. (내가 주로 떠올렸던 陰謀論(음모논)廣告(광고)를 더 많이 보게 하려고 다음 ()을 가끔만 表示(표시)한다는 것이었다.)

https://news.jtbc.co.kr/article/NB12245370

3

나는 버전 올리기 강박증같은게 있는데, RN 초기에 불안정한 라이브러리들 많이 쓰다가 생긴거 같다. 일단 버전 올린다음에 빌드 터지는지 기존 기능 잘돌아가는지 확인하는데, 이거하느라 쓰는 시간도 꽤 된다. 실제로 시간을 아끼고 있는지(모르던 버그를 모르고 해결해서) 아닌지 모르겠다.

3

GitHub 저장소 코드를 분석해 AI로 문서화하는 도구. 다이어그램을 적극적으로 활용해 아키텍처를 쉽게 이해할 수 있고, 문서의 깊이와 정확도도 높다. 여러모로 <오픈 소스 소프트웨어 아키텍처>를 읽으며 아쉬웠던 부분들을 커버해주는 프로젝트. deepwiki.com/

4

개발자의 저주: 고치는 능력을 가진 자의 무한한 책임감
------------------------------
- 사소한 자동화를 반복하다 보면 어느 순간 모든 도구와 시스템이 *고쳐야 할 대상* 으로 보이게 되는 *인지의 임계점* 에 도달하게 됨
- 기술력이 쌓일수록 문제를 단순히 인식하는 것을 넘어 *책임처럼 느끼게 되는 감정의 무게* 를 가지게 됨
- *고치고자 하는 욕구* 는 단순한 생산성 향상을 넘어서
감정…
------------------------------
https://news.hada.io/topic?id=20735&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

1
1

.cursorrules나 .windsurfrules 등의 파일에 프로젝트에 대한 설명을 써놓는것도 도움이 되나요? 공심 홈페이지의 가이드라인을 보면 코딩스타일 등의 지침만 써놓아서요. 어차피 알아서 일종의 인덱싱을 할테니 필요없을까요?

1

잉 drizzle에 Live Query 지원이 되네요? [join된 테이블의 reactivity에 문제가 있는데] 이건 고치면되는 버그같고요? 지금 만들고 있는거 왜 하고있지? 제가 원하는 추가 기능을 넣으려면 새로 짤 필요가 있을순 있지만 말이죠.

1
3

먼 미래에는 어떻게 될 지 잘 모르겠지만, 일단 코딩 에이전트한테 LSP를 툴로 쥐어 줘야 하는 게 아닌가 하는 생각이 요즘 많이 든다.

4

AI에 대한 SW 엔지니어들의 자신감은 "어쨌거나 업계 내에서 만드는거라서-" 인거 같다. 손바닥 위에 있다는 감각(얼추 맞긴 하다).

타 직업군은 AI나 LLM 솔루션 자체를 다루는데도 한계가 있거니와(아무래도 fork떠서 고친다거나 할순 없으니까) 결과물도 자기 의사와 관계 없이 학습당하고 있기 때문에…

아예 거스를 수 없는 것이기 때문에, 타 분야에서는 오히려 공격적으로 자기 분야에 특화된 모델을 만들고, 기존 저작물들을 학습으로 부터 보호해서 우선권을 선점 하는게 그나마 좀 더 낫지 않을까?

근데 후자는… 테크기업이 양아치라서 잘 안될거같다.

2
3

실제로 방금 어떤 사례를 발견했냐면, 계산이 살짝 까다로운 값에 대한 테스트를 만들라고 시켰더니 코드를 한 백줄 뱉어내는데

expect(x).toBe(42)

이렇게 값에 대한 테스트를 안하고

expect(typeof x).toBe("number")

이러고 넘어가려고 했다. 손바닥 이리내.

5

bgl gwyng shared the below article:

논리와 메모리 - 논리와 저수준(Low-level) 자료 표현(Data representation) (2 편 중 2 편)

Ailrun (UTC-5/-4) @ailrun@hackers.pub

이 글은 "논리적"이 되는 두 번째 방법인 논건 대수를 재조명하며, 특히 컴퓨터 공학적 해석에 초점을 맞춥니다. 기존 논건 대수의 한계를 극복하기 위해, 컷 규칙을 적극 활용하는 반(半)공리적 논건 대수(SAX)를 소개합니다. SAX는 추론 규칙의 절반을 공리로 대체하여, 메모리 주소와 접근자를 활용한 저수준 자료 표현과의 커리-하워드 대응을 가능하게 합니다. 글에서는 랜드(∧)와 로어(∨)를 "양의 방법", 임플리케이션(→)을 "음의 방법"으로 구분하고, 각 논리 연산에 대한 메모리 구조와 연산 방식을 상세히 설명합니다. 특히, init 규칙은 메모리 복사, cut 규칙은 메모리 할당과 초기화에 대응됨을 보여줍니다. 이러한 SAX의 컴퓨터 공학적 해석은 함수형 언어의 저수준 컴파일에 응용될 수 있으며, 논리와 컴퓨터 공학의 연결고리를 더욱 강화합니다. 프랭크 페닝 교수의 연구를 바탕으로 한 SAX는 현재도 활발히 연구 중인 체계로, ML 계열 언어 컴파일러 개발에도 기여할 수 있을 것으로 기대됩니다.

Read more →
12

After reviewing FEP-5624: Per-object reply control policies and GoToSocial's interaction policy spec, I find myself leaning toward the latter for long-term considerations, though both have merit.

FEP-5624 is admirably focused and simpler to implement, which I appreciate. However, 's approach seems to offer some architectural advantages:

  1. The three-tier permission model (allow/require approval/deny) feels more flexible than binary allow/deny
  2. Separating approval objects from interactions appears more secure against forgery
  3. The explicit handling of edge cases (mentioned users, post authors) provides clearer semantics
  4. The extensible framework allows for handling diverse interaction types, not just replies

I wonder if creating an that extracts GoToSocial's interaction policy design into a standalone standard might be worthwhile. It could potentially serve as a more comprehensive foundation for access control in .

This is merely my initial impression though. I'd be curious to hear other developers' perspectives on these approaches.

4
2
3

<section> 버리고 <article> 써야 하는 이유

〈section〉 태그 안의 글에 헤딩(제목)을 포함하면 화면상에서는 그 헤딩들이 논리적인 순차 구조를 가지고 있는 것처럼 보인다. 하지만 이는 순전히 시각적인 것일 뿐 보조 기술과 연동된 구조 정보가 아니다. 〈section〉 태그의 용도는 무엇이고, 헤딩을 어떻게 코딩해야 보조 기술 사용자에게 정말 중요한 구조 정보를 전달할 수 있을까?

3
5
1
2

똑같은 인터페이스에 대한 여러 구현체에 대해 같은 테스트를 적용하기위한 좋은 방법이 뭘까요? vitest의 경우에 test.each(implementations) 이런식으로 할수 있다는데, 이러면 구현체가 늘어났을때 테스트 파일을 수정해야하는점이 마음에 안든단 말이죠. 지금 구현체를 인자로 받아 테스트를 정의하는 함수를 만들고 각 구현체 마다 .test.ts파일을 만들어서 호출하는 방식을 고려하고 있습니다. 더 좋은 방법이 있을까요?

0

@bglbgl gwyng 커스텀 렌더러 (Solid에선 Universal Rendering이라고 부름) 지원 자체는 잘 되어 있는데 그냥 커뮤니티 망치가 부족해서 유지보수가 안 되는 것에 가깝고 😅 이런 물건은 왜인진 도저히 모르겠지만 나름 관리가 잘 되고 있습니다

3
2

캘린더 이벤트로 변환이 가능한 데이터들 (예약 서비스의 예약 내역, 캘린더 양식이 아니어서 변환이 필요한 데이터, 매우 낮은 에러 레이트 리밋(?) 같은 이유로 인하여 일반적인 캘린더 동기화에 넣기 불안한 출처)를 모아다가 주로 사용하는 캘린더 서비스로 보내주는 서비스를 만들려고 합니다.

3
1

현재 Hackers' Pub은 Fresh 2.0 알파 버전을 사용하고 있는데, Fresh 자체의 한계점도 많이 느꼈고 무엇보다 최근 몇 달 사이에 정식 릴리스를 향한 진전이 보이지 않기에 GraphQL 준비가 끝나면 프런트엔드를 SolidStart로 점진적으로 옮겨가고자 한다.

3
0

bgl gwyng 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

bgl gwyng shared the below article:

Hackers' Pub 업데이트: LLM 기반의 게시글 번역 기능

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

LLM 기반의 게시글 번역 기능이 추가되었습니다. 우선, 자신이 쓴 게시글이 LLM을 이용해 번역되는 것을 허용하려면, 게시글 공개 설정에서 “LLM 기반 자동 번역 허용” 옵션을 켜 주셔야 합니다. 기존 게시글은 모두 이 옵션이 꺼져 있습니다만, 새로 쓰는 게시글의 경우 기본적으로 켜져 있습니다.

한국어판 게시글 공개 설정 페이지에 추가된 “LLM 기반 자동 번역 허용” 옵션
한국어판 게시글 공개 설정 페이지에 추가된 “LLM 기반 자동 번역 허용” 옵션
영어판 게시물 공개 설정 페이지에 추가된 “Allow LLM-powered automatic translation” 옵션
영어판 게시물 공개 설정 페이지에 추가된 “Allow LLM-powered automatic translation” 옵션

위와 같이 옵션을 켜 준 게시글은 위쪽에 다음과 같이 “다른 언어로 읽기” 메뉴가 표시되게 됩니다. 이 메뉴에 나오는 언어 목록은 언어 설정에서 정할 수 있습니다.

게시글 첫 부분에 표시되는 “다른 언어로 읽기” 메뉴 (한국어판)
게시글 첫 부분에 표시되는 “다른 언어로 읽기” 메뉴 (한국어판)
게시글 첫 부분에 표시되는 “Read in other languages” 메뉴 (영어판)
게시글 첫 부분에 표시되는 “Read in other languages” 메뉴 (영어판)

이 중에서 이미 번역이 완료된 언어는 바로 표시되지만, 아직 번역이 완료되지 않은 언어의 경우, 아래와 같이 기다리라는 메시지가 뜨게 됩니다. 게시글의 분량에 따라 번역 시간은 차이가 나지만, 짧으면 30초에서 길면 5분 정도 걸립니다.

게시글이 번역중이라는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역중입니다. 번역이 완료될 때까지 기다려 주세요.”
게시글이 번역중이라는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역중입니다. 번역이 완료될 때까지 기다려 주세요.”
게시글이 번역중이라는 메시지 (영어판): “This article is being translated from Korean to English. Please wait until the translation is complete.”
게시글이 번역중이라는 메시지 (영어판): “This article is being translated from Korean to English. Please wait until the translation is complete.”

번역이 완료되면, 아래와 같이 메시지가 바뀝니다.

게시글의 번역본 상단에 뜨는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역되었습니다.”
게시글의 번역본 상단에 뜨는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역되었습니다.”
게시글의 번역본 상단에 뜨는 메시지 (영어판): “This article has been translated from Korean to English.”
게시글의 번역본 상단에 뜨는 메시지 (영어판): “This article has been translated from Korean to English.”

번역 기능은 제가 Hackers' Pub을 맨 처음 구상할 때부터 핵심 기능으로 고려하고 있던 것이었습니다. 소프트웨어 프로그래머로서 일정 수준 이상 성장하기 위해서는 반드시 영어를 배워야만 하는 불합리함이나 그리고 일본어나 중국어 등 영어가 아닌 언어로 쓰인 다양한 자료에 대부분의 외국인은 접근하지 못한다는 아쉬움을 오래 전부터 느꼈기 때문입니다. 다행히 얼마 전부터 LLM의 번역 품질이 아주 좋아졌고, 이를 활용하여 꽤 괜찮은 품질의 번역 기능을 Hackers' Pub 같은 작은 웹사이트에서도 구현할 수 있게 되었네요.

참고로 현재 번역에 쓰이는 모델은 Claude Sonnet 3.7입니다. 저렴하다고는 할 수 없는 모델인데요. 시범적으로 운영해 보고, 비용이 너무 부담된다고 여겨지면 Gemini 2.5 Flash 같은 다른 모델로 전환하는 것도 고려하고 있습니다.

아무튼, 모처럼 추가한 번역 기능이니 많은 분들이 유용함을 누리셨으면 좋겠습니다.

아래는 제가 샘플로 미리 만들어 둔 번역본들입니다:

  • Ditch the DIY Drama: Why Use Fedify Instead of Building ActivityPub from Scratch? (영어) → 〈DIY 드라마는 그만: 왜 ActivityPub을 처음부터 구축하는 대신 Fedify를 사용해야 할까요?〉 (한국어)
  • 〈애플리케이션 개발 측면에서 본 Drizzle ORM 대 Kysely 비교〉 (한국어) → 「アプリケーション開発の観点から見たDrizzle ORMとKyselyの比較」 (일본어)
  • 〈deno-task-hooks: Git 훅을 Deno 태스크로 쉽게 관리하기〉 (한국어) → deno-task-hooks: Easily Manage Git Hooks as Deno Tasks (영어)
  • Browser-Native Translation and Language Detection APIs Coming Soon (영어) → 〈브라우저 네이티브 번역 및 언어 감지 API 곧 출시 예정〉 (한국어)
Read more →
6

@hongminhee洪 民憙 (Hong Minhee) @bglbgl gwyng 처음 도커로 서비스를 구동할 때는 latest 태그를 붙여 사용했는데 버전 간 호환성 문제를 겪은 다음부터는 항상 정확한 버전을 가리키도록 태그를 만들어 사용하게 되었습니다. 근데 ... 확실히 매번 손으로 파일을 수정하고 손으로 docker compose pull && docker compose down && docker compose up 이라고 치는 건 ..... 개선 여지가 있다고 생각만 하고 있는데 ...... 뭘 해야 하는지는 잘 모르갰더라구요. 그.. 왓치타워 같은 솔루션이 있는 걸 알고는 있는데 제가 모를 때 그냥 버전이 띡 바뀌는 건 또 썩 맘에 안 들기도 하고요 ;;;;;;

@meWoojin Kim @hongminhee洪 民憙 (Hong Minhee) 제가 Nix가 minimal한 해결책이라고 했던건, Nix를 딱 그 이미지 태그가 박힌 docker-compose.yml을 출력(빌드?)하는데 까지 쓸수있다는 의미에서였습니다. 그러니까 배포하려는 서비스의 소스코드를 입력으로 주면 그걸로 빌드한 docker 이미지의 태그가 박힌 compose.yml을 구하는 함수를 정의할수 있어요.

2
1

LLM 시대 이후로 생산성과 관련해서 스트레스를 더 많이 받는거 같다. 당신이 오늘 허투루 보낸 하루는 누군가가 올바른 프롬프팅으로 +10K LOC를 할수있는 하루였다, 같은 생각을 퇴근하면서 한다.

3

큰 틀에서는 동의합니다. 파인만 아저씨 책을 보셨다니..며칠전 전공자들의 고집을 짚었던 걸로 보아, 전공은 아니신가 했는데, 셀프 디스를 하셨던 건가!) (다른 비함수형 언어와 달리) 워낙 IO란 걸, 잘 구분해야 하는 게, 쉽지 않은 미션인데, 시작을 너무 전통 hello world 에 얽매이는 건 아닐까 싶어서요. 굳이 쉽지 않은 걸 보여주고, 모르고 넘어가자라고 하느니, 차라리 더하기 하며, 로그 남기는 걸, 직접 순수 함수로 만들어 보는 게, 낫지 않을까 혼자 상상해 봤습니다. @bglbgl gwyng

@lionhairdino 아뇨ㅋㅋ물알못인데 그냥 친구가 책 빌려줘서 읽어봤습니다. IO를 쓰게한다고 해서 IO를 꼭 당장 설명해야하는건 아니니까요. 나중으로 미루면 되잖아요. 저는 당장 putStrLn이나 getLine를 못쓰게 해버리면 무슨 하자가 있는 언어라고 오해할까 걱정이 되네요ㅋㅋ

2
2
main = do
  n <- getLine
  putStrLn $ "hello " <> n

설명해야 될 게 한가득인데, 나중에, 나중에 하며 넘어가야 한다. 꼭 Lazy 평가처럼 말이다. 전통적인 "hello world"에서 출발하지 않아도 되는 것 아닐까? 첫 언어가 아닌 분들한테는, Writer 혹은 State 만들기부터 시작하면 어떨까? 단, "모나드"라고 말하지 않고.

@lionhairdino 사실 전 어떤 좋은 설명을 제공해야한다는 생각 자체에 좀 회의적입니다. 그냥 하다보면 알게되도록 보조해주는게 최선이라고 생각합니다. 약간 별개의 얘긴데, 옛날에 파인만 빨간책 읽다가 참 좋은 책이란 생각이 들었지만 설명이 너무 똑똑해서 덮었습니다.

1