26.9.9 / Wed

BaO·2026년 9월 9일

TIL

목록 보기
19/33

트랜잭션, ACID, ROLLBACK

ACID : 트랜잭션 원칙

  • A
    • 원자성 ( Atomicity ) : 모두 성공하거나 실패
  • C
    • 일관성 ( Consistency ) : 규제(제약조건)을 깨뜨리지 않음
  • I
    • 격리성 ( Isolation ) : 동시 트랜잭션이 서로를 간섭하지 않음
    • DB LOCK을 통해서 격리성 유지?
  • D
    • 지속성 ( Durability ) : Commit 된 것은 장애가 발생해도 남음

트랜잭션

  • BEGIN : 트랜잭션 시작
  • COMMIT : 트랜잭션 종료 / DB 영구 반영
  • ROLLBACK : 트랜잭션 복구 / COMMIT 전에만 가능

SUPABASE SDK

SUPABASE 연결하기

import os
from dotenv import load_dotenv
from supabase import create_client

# .env 파일의 환경변수 로드
load_dotenv()

url = os.getenv("SUPABASE_URL")
key = os.getenv("SUPABASE_PUBLISHABLE_KEY")

# Client 초기화
# supabase 클라이언트 객체 생성
supabase = create_client(url, key)

print(supabase)
# <supabase._sync.client.Client object at 0x10d2cc440>

SUPABASE SDK INSERT

# 사용자 추가
response = supabase.table('users').insert({"email" : "user03@test.org"}).execute()

# documents INSERT
response = supabase.table('documents').insert({
    'title' : '첫 번째 문서',
    'content' : '본문 내용',
    'user_id' : '1320f352-13eb-44fc-8662-3ae945e616cd'
}).execute()

SUPABASE SDK SELECT

# 회원 목록 조회
response = supabase.table('users').select('*').execute()
response.data

# ilike : 대소문자 구분없이 like문 실행
response = (supabase.table('users')
            .select('*')
            .ilike('email', 'user02%')
            .execute()
)


# single Keyword
response = (supabase.table('users')
            .select('*')
            .ilike('email', 'user04%')
            .single() # 레코드 한 개, 딕셔너리 형태로 반환
            # single 키워드 사용시에 레코드가 없거나, 1개 이상 존재하는 경우에 APIERROR 발생
            .execute()
)
response.data # API ERROR


# maybe_single Keyword
response = (supabase.table('users')
            .select('*')
            .ilike('email', 'user04%')
            .maybe_single() # 레코드가 0 ~ 1 일 때 사용, 레코드가 없다고 예외 발생 안함
            .execute()
)
response.data # None



# LIMIT Keyword
response = (supabase.table('users')
            .select('*')
            .ilike('email', 'user%')
            .limit(1) # like문을 사용할 떄 여러개의 레코드를 출력하면,
            #오류가 발생하므로 LIMIT 키워드를 이용해서 강제로 1개의 레코드만 출력
            .single()
            .execute()
)
response.data
# {'id': 'e8057300-87a3-43e2-aee6-e4b0d817d0b2',
#  'email': 'user01@test.org',
#  'created_at': '2026-09-08T03:00:44.799562'}


# OFFSET Keyword
response = (supabase.table('users')
            .select('*')
            .ilike('email', 'user%')
            .limit(1)
            .offset(1) # 1 인덱스에서 1개 조회
            .single()
            .execute()
)
response.data
# {'id': 'bd0f1b0d-5921-4751-9235-3e21dfb722ef',
#  'email': 'user02@test.org',
#  'created_at': '2026-09-09T00:42:22.44246'}


# ORDER BY Keyword
response =(
    supabase.table('users')
    .select('*')
    .order('email', desc=True) # email 컬럼 기준, 내림차순 정렬
    .execute()
)
response.data

# JOIN Keyword
## LEFT JOIN
response = (
    supabase.table('documents')
    .select('id, title, content, users(id, email)')
    .execute()
)
response.data

## INNER JOIN(!inner)
response = (
    supabase.table('documents')
    .select('id, title, content, users!inner(id, email)')
    .execute()
)
response.data

# join table 조건 걸기
response = (
    supabase.table('documents')
    .select('id, title, content, users!inner(id, email)')
    .ilike('users.email', 'user%')
    .execute()
)
response.data

SDK 기반 데이터 수정·삭제 구현

# UPDATE Keyword
response = (
    supabase.table('documents')
    .update({
        'title' : '첫 번째 문서(수정)'
    })
    .eq('id', 'd865df61-ecaa-4117-90e0-84d5a3726118')
    .execute()
)

# DELETE Keyword
response = (
    supabase.table('users')
    .delete()
    .eq('id', 'e8057300-87a3-43e2-aee6-e4b0d817d0b2')
    .execute()
)

SUPABASE Authentication

회원가입 & 로그인

key = os.getenv('SUPABASE_ANON_KEY')
supabase = create_client(url, key)

"""
supabase.auth : 인증(로그인) / 인가(접근 권한) 관련 메서드를 가지고 있는 객체

테이블 명 : auth.users
"""

# 회원가입
response = supabase.auth.sign_up({
    'email' : 'user01@test.org',
    'password' : 'password1234',
    'phone' : '010-1000-1000'
})

# 로그인
response = supabase.auth.sign_in_with_password({
    'email' : 'user01@test.org',
    'password' : 'password1234'
})
response.user

# 현재 로그인된 사용자 확인
user = supabase.auth.get_user() # 현재 로그인한 사용자 정보, 있으면 로그인, None이면 미 인증 상태
if user:
    print(f'로그인 상태 : {user}')
else :
    print(f' 미 로그인 상태 ')

# 로그아웃
supabase.auth.sign_out()

LLM

Langchain

  • 사용자의 자연어를 받아 프롬프트로 조립하고 모델을 호출하여 결과를 가공하는 소프트웨어
  • 개발자가 복잡한 문자열 처리와 수동으로 API를 호출하지 않도록 표준화된 파이프라인 제공

Langchain을 사용하는 이유

  • 순수 API 직접 호출의 한계

    • 매번 긴 문자열 포맷팅과 변수 주입 코드를 수동으로 작성해야 함
    • 모델 응답 객체에서 순수 텍스트만 꺼내기 위해서 딕셔너리 접근을 반복적으로 수행해야 함
    • 추후 문서 검색(RAG)나 도구 연동을 추가로 수행할 때 코드가 복잡해짐
  • 장점

    • 레고 블록 조립 방식 (LCEL)
      • 프롬프트 | 모델 | 파서 형태로 파이프 기호를 통해 데이터 흐름을 한눈에 파악 가능
    • 입출력 표준화
      • 변수를 프롬프트에 안전하게 주입하고, 모델 응답에서 텍스트만 추출 가능
    • 일관된 확장성
      • 체인 구조 그대로 RAG, 구조화 출력, AI 에이전트로 자연스럽게 확장 가능
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate

# system : 역할, 상황, 제한 조건, 예시 -> 시스템 메세지, SystemMessage(..)
# user : 사용자의 질의 / HumanMessage(..)
# asistant : AI 답변 / AIMessage(..)

# 시스템에게 역할, 상황, 제한 조건, 예시 등 제시
prompt_template = ChatPromptTemplate.from_messages([
    ('system', "당신은 컴퓨터 기초 개념을 일상적인 사물에 빗대어 설명하는 교육 전문가입니다. 2문장 이내로 친절하게 설명하세요."),
    ('user', '{user_question}')
])

# 샘플 프롬프트 선언
sample_prompt = prompt_template.invoke({
    "user_question" : "프로그래밍에서 '변수'가 무엇인가요?"
})

# 모델 호출
model = ChatOllama(
    model='mistral',
)

# 모델에 샘플 프롬프트 삽입
response = model.invoke(sample_prompt)
# 순수 텍스트만 추출하기
from langchain_core.output_parsers import StrOutputParser


parser = StrOutputParser()
text = parser.invoke(response)
# 파이프 연산자를 사용해서 3개의 컴포넌트를 하나의 파이프라인으로 연결
chain = prompt_template | model | parser
res = chain.invoke({
    "user_question" : "'클래스'가 뭔데'"
})

res

0개의 댓글