Hasura 트랜잭션 처리: Actions와 Functions 활용하기📌 TL;DR (한 줄 요약)Hasura v2는 단일 mutation 내부는 자동 트랜잭션이지만,복잡한 로직은 Actions 또는 PostgreSQL Functions로 처리해야 합니다.🎯 핵심 정리단일 GraphQL mutation은 자동으로 트랜잭션 처리여러 mutation을 별도 호출하면 트랜잭션 아님Hasura Actions로 커스텀 비즈니스 로직 구현 (추천)PostgreSQL Functions (RPC)로 DB 레벨 트랜잭션 처리Apollo Client의 배치는 서버 트랜잭션이 아님1. Hasura의 트랜잭션 동작 방식✅ 자동 트랜잭션 (보장됨)# 하나의 mutation 요청 = 하나의 트랜잭션mutation Create..
PostgREST와 Hasura의 관계Hasura는 PostgREST를 사용하지 않습니다.Hasura와 PostgREST는 완전히 별개의 프로젝트!차이점 비교아키텍처Supabase:PostgreSQL → PostgREST → REST API → Supabase JS ClientHasura:PostgreSQL → Hasura Engine → GraphQL API → Apollo Client기술 스택 SupabaseHasura프로토콜REST (PostgREST 사용)GraphQL (자체 엔진)쿼리 언어URL + 체이닝GraphQL엔진PostgREST (Haskell)Hasura Engine (Haskell)철학REST + GraphQL 스타일 쿼리순수 GraphQL참고 블로그 글의 의미PostgREST? "..
Supabase의 PostgREST: GraphQL처럼 쿼리하는 SQL📌 TL;DR (한 줄 요약)Supabase는 PostgREST를 통해 GraphQL의 선언적 쿼리 스타일을 REST API로 구현하여, SQL JOIN을 직관적인 체이닝 메서드로 사용할 수 있게 합니다.🎯 핵심 정리Supabase JS는 SQL을 직접 쓰지 않고 체이닝 메서드 사용PostgREST는 GraphQL의 장점과 REST의 단순함을 결합한 프로토콜Foreign Key 기반으로 자동 관계 생성 - 별도 설정 불필요중첩 쿼리로 복잡한 JOIN도 간단하게 표현TypeScript 타입 자동 생성으로 완벽한 타입 안정성1. SQL JOIN의 전통적 방식전통적인 SQL JOIN-- 게시글 + 작성자 정보SELECT posts.i..
Supabase RPC: 트랜잭션과 보안을 위한 필수 도구📌 TL;DR (한 줄 요약)Supabase RPC는 성능 최적화뿐만 아니라 Row Level Security 하에서 안전한 트랜잭션 처리를 위한 핵심 기능.🎯 핵심 정리Supabase JS 클라이언트는 트랜잭션을 지원하지 않음RPC (PostgreSQL Functions)를 사용하면 서버 측에서 트랜잭션 보장RLS (Row Level Security)는 행 단위 접근 제어로 클라이언트의 직접 DB 접근을 안전하게 보호RLS 정책의 USING 절은 관계 테이블을 통한 복잡한 권한 체크 가능트랜잭션 + RLS를 함께 사용하면 안전하고 일관된 데이터 처리 가능1. 문제 상황: Supabase JS의 한계Supabase JS는 트랜잭션을 지원하지 않..
Terraform으로 Neon DB + Hasura GraphQL API 서버 구축하기프로젝트 개요백엔드 인프라: Neon PostgreSQL + Hasura (EC2) + AWS Secret Manager + IAM프론트엔드: Next.js (Vercel 배포 예정) + Apollo Client + GraphQL Code Generator패키지 매니저: pnpm1. 인프라 구축 (Terraform)1.1 사전 준비Neon DBneon.tech 가입 및 프로젝트 생성Connection string 복사 postgresql://[user]:[password]@[endpoint]/[dbname]?sslmode=requireSSH 키 준비# SSH 키가 없다면 생성ssh-keygen -t rsa -b 4096..
TanStack Query DevTools vs Apollo DevTools 비교TanStack Query DevTools ✨import { ReactQueryDevtools } from '@tanstack/react-query-devtools' // 기능:// ✅ 모든 쿼리 상태 실시간 확인// ✅ 캐시 데이터 탐색// ✅ 쿼리 무효화/리페치 버튼// ✅ 타임라인// ✅ 직관적인 UIApollo DevTools 😢// 브라우저 확장 프로그램 설치 필요// Chrome/Firefox Extension// 기능:// ⚠️ 쿼리 목록 (기본적)// ⚠️ 캐시 탐색 (복잡함)// ⚠️ Mutation 추적 (불편함)// ⚠️ UI가 투박함// ❌ 타임라인 없음// ❌ 실시간 업데이트 약함해결책: Cus..
문제 상황일반적인 Next.js + Apollo 설정// lib/apollo-client-rsc.ts (Server Component용)import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'import { registerApolloClient } from '@apollo/experimental-nextjs-app-support/rsc'export const { getClient } = registerApolloClient(() => { return new ApolloClient({ cache: new InMemoryCache(), link: from([ loggerLink, // 중복 1 err..
ApolloLink = GraphQL 요청/응답 미들웨어Express.js의 미들웨어와 완전히 같은 개념이에요:Express.js 미들웨어// Expressapp.use((req, res, next) => { console.log('Request:', req.url) // 로깅 next() // 다음 미들웨어로})app.use((req, res, next) => { req.user = getCurrentUser() // 인증 next()})app.use((req, res, next) => { if (!req.user) { return res.status(401).send('Unauthorized') // 권한 체크 } next()})Apollo Link (같은 패턴!)// Apo..
캐시 키 비교TanStack Query// 명시적으로 캐시 키 지정useQuery({ queryKey: ['campaigns', { status: 'open', page: 1 }], queryFn: () => fetchCampaigns({ status: 'open', page: 1 })})// 캐시 구조{ '["campaigns",{"status":"open","page":1}]': { data: [...], ... }, '["campaigns",{"status":"open","page":2}]': { data: [...], ... },}Apollo Client// 캐시 키 자동 생성!useQuery(GET_CAMPAIGNS, { variables: { status: 'open', page: 1..
1. InMemoryCache의 typePolicies기본 개념: Apollo Client의 캐싱 메커니즘Apollo Client는 GraphQL 응답을 메모리에 캐싱해서 같은 데이터를 다시 요청할 때 네트워크 요청 없이 즉시 반환.const cache = new InMemoryCache({ typePolicies: { Query: { fields: { campaign_application: { keyArgs: ['where', 'order_by'], merge(existing, incoming) { return incoming }, }, }, }, },})상세 분석typePoli..
Hasura의 특별한 점일반 GraphQL 서버 (Apollo Server 등):GraphQL → 직접 작성한 리졸버 → SQL 쿼리 (여기서 DataLoader 필요!)Hasura:GraphQL → Hasura 엔진 → 최적화된 SQL 자동 생성 (DataLoader 불필요! 이미 내장됨)Hasura가 자동으로 해주는 것들1. 자동 JOIN 최적화query GetCampaign { campaign(where: { id: { _eq: "abc-123" } }) { id title applications { id status influencer { id username platforms { ..
Hasura(v2) GraphQL + Next.js + Apollo ClientHasura(v2) 로 GraphQL API서버를 만들고, DB는 local PostgreSQL 을 docker 로 띄울 것입니다.대충 반려동물 주제로 유저, 포스트, 좋아요, 댓글, 동물정보 테이블을 만들고,seeding은 faker.js 이용하여 typescript 로 합니다. 쿼리와 뮤테이션 작성후 codegen까지 완료되면 Next.js 15 app router 에 맞게apollo cilent 설정을 하고 프론트 개발을 하면 됩니다.순서프로젝트 init의존성 설치여기 참고하여 curl 로 docker-compose 파일 받기docker 띄우기(시딩용 ports: - "5432:5432" 추가필요)localhost:808..
axios client 의 실전적 구성1. 문제 정의axios 클라이언트 구성을 매번 그때그때 하다보니 문제가 생길때가 많음그러다보니 구조화가 되어있지 않아 어떻게했었지 하고 또 찾아봄요청, 응답 interceptor 작성을 관성적으로 했더니 사용할때 제네릭을 두번씩 api.get 이런식으로 쓰고 있었음2개 이상의 axios 클라이언트를 만들때(ex 서버용, 클라이언트용 등) 계층화가 안되어있어 불편타입 안전성 부족과 에러 처리가 일관성이 없음환경별 설정 관리가 어려움2. 해결 방안클래스로 axios api 클라이언트를 구성한다api-client 추상 클래스 에서는 axios instance 를 생성하고 기본 메서드를 오버라이드한다기본메서드를 오버라이드 하는 이유는 사용시 제네릭 입력 두번 안하고 편하게..
Biome 로 테일윈드 클래스 저장시 자동정렬하기biome.jsonclevel을 errorfix를 safe"nursery": { "useSortedClasses": { "level": "error", "fix": "safe", "options": { "attributes": [ "classList" ], "functions": [ "clsx", "cva", "cn" ] } }}.vscode/settings.jsoneditor.codeActionsOnSave에 아..
Next.js static build + s3 조합에서 라우팅이 안된다?그렇습니다. 아래처럼 next.config 설정하고const nextConfig: NextConfig = { output: "export", // 정적 파일만 trailingSlash: true, images: { unoptimized: true },};빌드하여 s3 에 올리고 CF 붙이면메인 index.html 은 잘 나오는데 라우팅이 안됩니다.클로드쌤과 함께 해결했습니다.CloudFront FunctionsLambda@Edge보다 비용이 저렴한 CloudFront Functions를 사용해 보았습니다.function handler(event) { var request = event.request; var..
DNS와 hosts 파일 동작 원리1. 일반적인 DNS 조회 과정브라우저 → hosts 파일 확인 → DNS 서버 조회 → IP 주소 반환 → 웹사이트 접속2. hosts 파일 수정 후 과정브라우저 → hosts 파일에서 직접 IP 발견 → DNS 서버 건너뛰고 바로 해당 IP로 접속hosts 수정하여 사용하기 단계별 정리단계 1: 현재 상황 파악원래 도메인(예: example.com)이 CloudFront → S3를 가리킴하지만 Cafe24 서버(000.000.000.000)의 내용을 확인해야 함단계 2: hosts 파일 수정로컬 hosts 파일에 다음과 같은 내용 추가:000.000.000.000 example.com단계 3: 결과브라우저에서 example.com 입력 시 DNS를 거치지 않고 바로 ..
패스워드 저장 방법패스워드는 절대 평문으로 저장하면 안 되고, 해싱해서 저장해야 합니다.1. bcrypt 설치pnpm add bcryptpnpm add -D @types/bcrypt2. 회원가입 시 패스워드 해싱// app/api/auth/signup/route.tsimport bcrypt from 'bcrypt';import { prisma } from '@/lib/prisma';export async function POST(request: Request) { const { email, nickname, password } = await request.json(); // 패스워드 해싱 (saltRounds: 10~12 권장) const hashedPassword = await bcrypt.has..
질문:스키마 새로 작성시 마이그레이션, 클라이언트 생성 은 맨날 반복하면 되남유?답변:네 맞습니다! 스키마를 변경할 때마다 반복해야 해요.스키마 변경 시 워크플로우# 1. schema.prisma 수정# 2. 마이그레이션 생성 & 적용npx prisma migrate dev --name add-user-model# 3. Prisma Client 재생성 (보통 자동으로 됨)npx prisma generate중요한 점들migrate dev의 장점npx prisma migrate dev는 마이그레이션 파일 생성 + DB 적용 + 클라이언트 생성을 한 번에 처리해요--name 옵션으로 마이그레이션에 의미있는 이름을 부여하세요실제 사용 예시# 처음 User 모델 만들 때npx prisma migrate dev -..
SSO vs OAuthSSO(Single Sign-On)와 OAuth는 인증(Authentication)과 인가(Authorization) 관점에서 서로 다릅니다. 간단히 비교하면 다음과 같습니다:✅ 목적의 차이항목SSO (Single Sign-On)OAuth (Open Authorization)핵심 목적하나의 로그인으로 여러 서비스 사용제3자 앱이 사용자의 자원에 접근 허용주요 개념인증(Authentication) 중심인가(Authorization) 중심✅ 예시SSO:회사 포털에 한 번 로그인하면 이메일, 회계, 인트라넷 등 다양한 시스템에 추가 로그인 없이 접근 가능OAuth:어떤 앱이 “구글 계정으로 로그인”할 때, 구글에서 인증하고 → 해당 앱이 사용자 이메일, 프로필 등 제한된 정보에 접근하도록..
질문:seed.ts 를 작성했는데, 이거 자동 반영되나유??답변:아니요! 시드 스크립트는 앱 시작과는 별개로 수동 실행해야 해요.시드 실행 방법1. 직접 실행npx tsx seed.ts# 또는node --loader ts-node/esm seed.ts2. package.json에 스크립트 추가 (권장){ "scripts": { "dev": "next dev", "build": "next build", "seed": "tsx seed.ts" }}그러면 이렇게 실행:pnpm run seed3. Prisma 공식 방법package.json에 prisma 설정 추가:{ "prisma": { "seed": "tsx prisma/seed.ts" }}그러면 이렇게 실행:npx prisma..
질문:src/generated/prisma/index.d.ts 에 다음 코드가 있는 걸 확인 했는데, export type Post = $Result.DefaultSelectionsrc/generated/prisma 는 gitignore 처리 되어 있어요.그러면 이거.. git 에 올라가지 않으니 vercel 배포시 오류날 것 같은데,저 export type Post 부분을 prisma 에서 제공하는 인터페이스? 등을 활용해서 제가 작성해야 하나유? 답변:아니요! Vercel 배포 시 자동으로 해결돼요. 걱정하지 마세요!Vercel 배포 과정1. 빌드 시 자동 생성// package.json{ "scripts": { "build": "next build", "postinstall": "pri..
RSC에서 QueryClient 패턴질문: prisma client 혹은 다른 여러 이런 종류 클라이언트들은 singleton 으로 쓰는 경우 많은데,왜 tanstack-query server side 에서는 매번 new QueryClient() 하나유?왜 매번 새로 생성하나요?// 각 요청마다 독립적인 QueryClient가 필요async function PostPage() { const queryClient = new QueryClient() // 서버 요청별로 새 인스턴스 await queryClient.prefetchQuery({...}) return 이유서버사이드는 stateless: 각 요청이 독립적요청간 격리: 다른 사용자의 데이터가 섞이면 안됨메모리 누수 방지: 요청 완료 후 GC로 ..
Q1) 파이썬에는 반복문이 for in 밖에 없나요?A1) while 도 있습니다.아니요, 파이썬에는 for in 외에도 while 반복문이 있습니다.파이썬의 주요 반복문:for 반복문# 리스트 순회fruits = ['apple', 'banana', 'orange']for fruit in fruits: print(fruit)# range 사용for i in range(5): print(i)# 딕셔너리 순회person = {'name': '오이', 'job': '개발자'}for key, value in person.items(): print(f"{key}: {value}")while 반복문# 조건이 참인 동안 반복count = 0while count 반복문 제어문:break: 반복문 즉시 ..
Q1) schemas.py == dto?파이썬 자체에 대한 질문이라기 보다는 nest.js 와의 비교 일것 같은데, schemas.py 에서 정의한 class 들은 nest.js 의 dto 같은 거라고 봐야 할까요?A1)정확합니다! 💯 schemas.py의 클래스들은 NestJS의 DTO와 거의 동일한 역할을 합니다!NestJS DTO vs FastAPI Pydantic SchemasNestJS DTO 예시// create-book.dto.tsimport { IsString, IsOptional, IsInt, Min, Max, Matches } from 'class-validator';export class CreateBookDto { @IsString() @Length(1, 200) title:..
Q1) 파이썬 모듈 시스템?아무래도 파이썬에 아직 익숙하지 않고 자바스크립트 베이스다보니 export import 가 어떻게 이루어지는지 잘 모르겠어요. 예를들어 main.py 에서는from fastapi import FastAPI, HTTPException, Depends, statusfrom sqlalchemy.orm import Sessionfrom typing import List from . import crud, models, schemasfrom .database import SessionLocal, engine, get_dbfrom .config import settings이렇게 되어 있는데, config 는 .config 로 가져오고, crud, models, schemas 는 왜 . 으..
Q1) get_db 에서 제너레이터를 반환하는 이유가 궁금해요A1) 제너레이터 설명훌륭한 질문입니다! get_db 함수와 제너레이터 패턴에 대해 자세히 설명드리겠습니다.1. get_db 함수의 역할기본 구조def get_db(): db = SessionLocal() # 1. DB 세션 생성 try: yield db # 2. 세션을 "빌려줌" finally: db.close() # 3. 반드시 세션 종료함수의 생명주기요청 시작: 새로운 DB 세션 생성요청 처리: 라우터 함수에 세션 전달요청 완료: 자동으로 세션 종료2. 제너레이터를 사용하는 이유일반 함수 vs 제너레이터 비교❌ 일반 함수로 하면 어떻게 될까?def get_db_wrong(..
uv를 사용한 FastAPI 도서 관리 API 만들기1단계: uv 설치 및 프로젝트 초기화uv 설치# uv 설치brew install uv프로젝트 초기화uv init fastapi-practicecd fastapi-practice2단계: 필요한 의존성 추가# FastAPI 관련 패키지들 (따옴표 필수!)uv add fastapi "uvicorn[standard]"# 데이터베이스 관련uv add sqlalchemy "psycopg[binary]" alembic# 환경변수 관리uv add python-dotenv# 개발용 도구uv add --dev pytest httpx주의: zsh 쉘에서는 []가 포함된 패키지명을 반드시 따옴표로 감싸야 합니다!3단계: DB - Docker 설정 (PostgreSQL)ve..
macOS에서 여러 GitHub 계정 사용하는 방법 (SSH 중심)회사 계정과 개인 계정을 구분해서 GitHub를 쓰고 싶을때..매번 어떻게 어떻게 했었는데 정리가 안되었어서 이번기회에 정리합니다.macOS 환경에서 SSH 방식으로 GitHub 여러 계정을 깔끔하게 설정하는 방법✅ 방법 개요GitHub 계정별로 별도의 SSH 키를 만들고, ~/.ssh/config에 설정해주는 방식입니다.왜 SSH가 더 나은지?토큰 입력 없이 인증 자동화계정별로 구분해 push/pull 가능한 번만 설정하면 편리하게 사용 가능1️⃣ 계정별 SSH 키 생성# 개인 계정ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/id_ed25519_personal# 회사 계정ss..
Docker Desktop에서 이미지가 "in use" 상태로 표시되어 삭제되지 않는 문제1. 숨겨진 컨테이너 확인 및 정리터미널에서 다음 명령어로 모든 컨테이너(중지된 것 포함)를 확인해보세요:# 모든 컨테이너 확인 (중지된 것 포함)docker ps -a# 중지된 모든 컨테이너 삭제docker container prune -f2. Dangling 이미지 정리태그가 없는 이미지들을 정리합니다:# dangling 이미지 확인docker images -f "dangling=true"# dangling 이미지 삭제docker image prune -f3. 네트워크 및 볼륨 정리이미지가 네트워크나 볼륨에 연결되어 있을 수 있습니다:# 사용하지 않는 네트워크 정리docker network prune -f# 사..
JSON-LD: 웹 표준에서 AI 검색 기반으로JSON-LD는 단순한 웹 표준에서 AI 기반 검색 최적화의 핵심으로 변모했습니다. 2011년 JSON과 시맨틱 웹 기술을 연결하기 위해 설계된 JSON-LD는 이제 지식 그래프, AI 크롤러, 생성형 검색 엔진의 중요한 인프라 역할을 하고 있습니다. 이러한 진화는 기계가 웹 콘텐츠를 이해하고 처리하는 방식에서 가장 중요한 변화 중 하나를 나타냅니다.기원과 표준화: 링크드 데이터를 접근 가능하게 만들기JSON-LD는 2011년 시맨틱 웹 기술을 단순화해야 한다는 절실한 필요에서 출현했습니다. Digital Bazaar의 Manu Sporny가 프로젝트를 시작했고, 공동 편집자인 Gregg Kellogg, Markus Lanthaler, Dave Longley..