프론트엔드

BO에서 글을 수정해도 Web이 안 바뀌는 이유: 모노레포 환경 캐시 전략

BO에서 글 수정 시 Web에 미반영 문제 관리자 페이지(BO)에서 게시글을 수정하면 DB 및 BO에는 즉시 반영된다. 문제는 수정 직후 Web에서 해당 글을 확인하면, 내용이 이전 버전으로 계속 노출되었다. 관리자에서 수정이 완료되었는데 Web에는 반영되지 않는 상황이기 때문에, 운영 흐름상 수정 직후 화면에 변경 사항이 보이지 않는 것은 문제가 될 수밖에 없다. Web은 Next.js App Router + fetch 기반 SSR 구조로 구성되어 있었고, App Router의 fetch는 서버 레벨 캐싱을 기본으로 동작한다. 이는 일반적인 클라이언트 캐싱 라이브러리(예: React Query)와는 동작 방식이 다르다. App Router 환경에서 fetch는 기본적으로 force-cache로 동작한다. 단순 새로고침은 서버 재요청을 의미하지 않는다. 데이터는 빌드 시점 혹은 ISR 주기에 따라 캐시된다. 명시적으로 무효화하지 않는 한 캐시는 유지된다. 따라서 DB가 갱신되더라도, Web 서버의 캐시가 무효화되지 않으면 화면에는 반영되지 않는다. Web과 BO가 분리된 모노레포 구조 프로젝트는 pnpm 기반 모노레포로 구성되어 있다. 구조적으로 다음과 같은 특성을 가진다. web: SSR 기반 콘텐츠 렌더링 bo: CRUD 중심 관리자 기능 api: 데이터 처리 백엔드 이 세 애플리케이션은 같은 레포에 존재하지만, 런타임은 완전히 독립적으로 동작한다. 즉, 모노레포는 코드 레벨의 공유를 의미할 뿐, 런타임 공유를 의미하지 않는다. BO에서 게시글을 수정하면 DB는 갱신 되지만, Web 서버 내부 캐시는 별도의 프로세스로 유지된다. 따라서 BO에서 직접 revalidate를 호출하더라도 Web 서버의 캐시는 무효화되지 않는다. 설계한 캐시 무효화 전략 ① Web에 invalidation 전용 API route 생성 Web 서버 내부에서만 실행되는 캐시 무효화 엔드포인트를 별도로 구성하였다. 캐시 무효화는 반드시 Web 서버 내부에서 실행되도록 설계하였다. ② BO → API → Web Invalidation API 호출 게시글 생성/수정/삭제 성공 이후 BO가 호출한 백엔드 api는 Web의 invalidation API를 호출한다. 이를 통해 캐시 무효화 요청이 Web 서버로 전달된다. ③ Web 서버 내부에서 revalidateTag 실행 Web 서버 내부에서 revalidateTag를 호출하여 도메인 단위 캐시를 선택적응로 무효화한다. 게시글 상세는 도메인 단위 태그 + ID 단위 태그를 함께 사용했다. 전체 흐름은 다음과 같다. contracts 패키지에 revalidate 전략 포함하기 이 과정에서 contracts 패키지를 단순 타입 공유 계층으로 사용하지 않았다. 캐시 무효화 전략을 API 계약의 일부로 정의하였다. API contract는 다음과 같은 요소를 포함하였고, 캐시 전략을 위해서 여기에 revalidate 전략을 포함시켰다. HTTP Method Path Request Params / Body Response 타입 revalidate 즉, 특정 API가 호출되었을 때 어떤 캐시 태그가 무효화되어야 하는지까지 명시했다. 예시: UpdatePost Contract 설계 의도 이를 통해 다음과 같은 효과를 얻었다. BO와 Web이 동일한 무효화 규칙을 공유 도메인 단위 무효화 정책의 재사용 가능 캐시 전략을 명시적 설계 요소로 승격 결과적으로 contracts는 단순한 타입 패키지가 아니라 애플리케이션 간 계약을 정의하는 계층으로 확장되었다. 회고 이전에도 모노레포 구조를 구성해본 적은 있었지만, 두 개 이상의 애플리케이션을 동시에 개발하고 실제 데이터 흐름과 런타임 경계를 고려하며 설계한 것은 처음이었다. 특히 web, bo, api 세 애플리케이션과 contracts, ui 패키지가 유기적으로 연결된 구조에서 타입 공유만으로는 해결되지 않는 문제들이 드러났다. 같은 레포에 있다는 사실이 곧 실행 환경까지 공유한다는 의미는 아니었다. 런타임은 명확히 분리되어 있었고, 이번 캐시 이슈는 그 차이를 분명하게 보여준 사례였다. 또한 콘텐츠 운영 환경에서는 단순히 CRUD를 구현하는 것만으로는 충분하지 않다는 점을 알게 되었다. 데이터를 저장하는 것과 사용자 화면에 반영되는 것 사이에는 여러 계층이 존재한다. 이번 경험은 기능 구현을 넘어서 운영을 고려한 설계에 대해 고민하게 된 계기였다.

2026년 02월 19일 16:20

프론트엔드

URL을 상태로 관리한 방법: TanStack Router

인턴 기간 동안 백오피스 프로젝트에서 React Router 기반 레거시 코드를 마이그레이션 하는 작업을 맡았다. 단순히 마이그레이션 하기 보다는, 기존 JavaScript의 상태를 TypeScript로 마이그레이션 하면서 어떻게 런타임과 컴파일 타임 둘다 안정성을 잡을 수 있을지에 대한 고민이 많았다. 레거시 React Router 구조 기존 구조에서는 많은 필터 값을 url에 올리지 않고, 내부 상태로만 관리하고 있었다. 여기에서의 문제점은 사용자가 새로고침 시, 설정해놓은 필터 값들이 사라진다는 것이다. 따라서 UX를 향상하기 위해 사용자가 필터 값들을 새로고침 시에도 상태가 변경되지 않게 URL에 올리기로 결정하였다. URL로 상태를 옮기며 마주한 문제 ① 타입 안정성 저하 예를 들어 이런 URL이 들어올 수 있다 page, size는 number 타입이어야 하지만 실제로 사용자가 임의로 주소창에 입력한 값은 string이다. 사용자가 직접 값을 주입하였을 때, 서버로 값이 들어갈 수 있다는 점에서 라우터 단의 타입 관리가 필요해보였다. ② 상태의 기준점 모호 URL에 필터값을 올리는 방식을 채택하였을 때 필터 상태가 다음과 같이 여러 군데에 흩어져 있는 문제가 있다. useState로 관리되는 내부 상태 서버 요청 시 사용하는 query 객체 URL search 파라미터 따라서 이 기준점을 어디에 잡아야할지도 논의 및 합의가 필요한 부분이었다. 라우터 단에서 상태를 정규화하기 TanStack Router는 React Router와 달리 라우터 단에서 validateSearch를 제공한다. search 파라미터를 읽는 것과 동시에, 컴파일 및 런타임에서 zod로 검증 및 정규화를 수행할 수 있다. 라우터 단에서 다음의 기능을 수행하였다. 허용된 값만 통과 타입 자동 추론 실패 시 기본값 정규화 잘못된 쿼리는 서버에 전달되지 않음 상태 업데이트 규칙을 통일하기 URL 쿼리 값 정규화 문제는 해결하였지만, 가장 큰 문제는 필터가 있는 화면에서 코드가 계속 반복된다는 점이었다. 부분 업데이트 로직 기본값 처리 URL과 useState, URL과 서버 상태의 동기화 로직 이런 로직들이 페이지마다 반복되고 있었으며, 개발자마다 다른 로직을 사용하여 코드를 알아보기에도 쉽지 않았다. 이 문제는 useSearchParams 커스텀 훅을 만들어 해결하였다. useSearchParams Custom Hook 이 훅의 목적은 단순하다. search는 항상 타입 안전해야 하고 업데이트 방식은 팀 내에서 동일해야 한다. ① URL에 들어갈 수 있는 타입 제한 Date, 객체, Map 같은 값은 애초에 타입 단계에서 차단했다. search는 URL 직렬화 가능한 원시값만 가진다는 규칙을 강제했다. ② 업데이트 방식을 통일 이 구조로 인해서 페이지에서 search를 다루는 방식이 완전 동일해졌다. 부분 업데이트는 applySearch 전체 리셋은 resetSearch undefined는 무시 defaultSearch는 항상 기준점 적용 이후의 변화 이 커스텀 훅을 사용함으로써 다음의 변화가 생겼다. 프론트엔드 개발자 3명이 동일한 navigation 규칙 사용 필터 로직 중복 코드 약 40% 감소 공유, 새로고침, 뒤로가기 상황에서 상태 유지 신규 페이지 추가 시, navigation 로직 추가 간편화 회고 URL을 상태로 쓰는 건 생각보다 까다롭다. 페이지 내부 상태로만 관리할 때는 "내가 넣은 값만 존재한다"는 가정이 가능하지만, URL로 올리는 순간부터는 “누군가 아무 값이나 넣을 수 있다” 는 전제가 시작된다. 그래서 오히려 상태가 더 불안정해질 수도 있다. 재미있게도, 이런 종류의 문제는 결국 라이브러리 선택보다 팀 규칙의 문제다. 정규화 지점이 어디인지, 기본값이 무엇인지, 업데이트가 어떤 규칙을 따르는지. 이걸 문서가 아니라 코드로 강제했을 때 팀 생산성이 가장 크게 달라졌다. 전체 useSearchParams 코드 ts const { search, applySearch, resetSearch } = useSearchParams({ defaultSearch: { page: 1, keyword: '' }, Route, }) applySearch({ keyword: 'abc', page: 1 }) applySearch({ keyword: '' }) // keyword 제거 의도 applySearch({ tags: ['a', '', 'b'] }) // tags -> ['a','b'] applySearch({ tags: [] }) // tags 제거 의도 resetSearch() *

2026년 02월 12일 03:56

프론트엔드

토큰 만료(401)와 인터셉터 재설계 기록

백오피스를 마이그레이션하면서 가장 먼저 마주한 문제는 401이었다. 단순한 인증 에러처럼 보였지만, 실제로는 인증 흐름 전반을 다시 설계해야 하는 문제였다. 이번 글은 기존 코드를 그대로 사용하는 대신, Axios 인터셉터와 토큰 갱신 전략을 처음부터 다시 설계한 과정에 대한 기록이다. 기존 코드 사용으로 시작한 인터셉터 초기에는 기존 프로젝트의 Axios 인터셉터를 그대로 가져와 사용했다. 기능 자체는 동작했지만, 내부 동작 원리를 충분히 이해한 상태는 아니었다. 코드 리뷰에서 “구조를 이해하고 다시 설계해보자”는 피드백을 받았고, 그 시점에서 인터셉터의 전체 인증 흐름을 다시 정리하기 시작했다. 마침 면접 당시 받았던 질문이 떠올랐다. 사실 이번 인턴 면접에서 토큰 기반 인증에 관한 질문이 있었고, 한 페이지에서 여러 요청이 동시에 401이 발생하면 어떻게 처리할 것인가? 그때는 명확히 답하지 못했지만, 이번에 실제로 같은 상황을 마주하게 되었다. 첫 문제: 401 이후 403 예상 시나리오는 다음과 같았다. 오래된 at로 api 호출 서버가 401 던짐 인터셉터가 refresh 호출 새 토큰으로 요청 재시도 … 되어야 하는데 accessToken 만료 → 401 자동 refresh 호출 → 403 원인을 확인해보니 accessToken과 refreshToken의 만료 시간이 동일하게 설정되어 있었다. accessToken이 만료되는 시점에는 이미 refreshToken도 만료된 상태였다. at가 만료되었을 때 rt도 만료가되어 refresh를 할 수 있는 시간이 없었던 것이다. accessToken 만료 → refresh 요청 시도 근데 refreshToken도 이미 만료 → 403 그래서 당연히 403이 나는 구조였다. 근데 백엔드를 바꿔달라고 할 수가 없는 상황 이걸 계기로 오랜만에 조금 머리를 굴려보았다. 자동 재발급 전략 재설계 처음에는 이런 식의 자동 로직을 생각했다. accessToken 만료 5분 전에 자동으로 refresh 수행 하지만 백오피스 특성상, 사용자가 아무 행동을 하지 않아도 토큰을 갱신하는 방식은 적절하지 않았다. 그래서 방향을 바꿨다. 사용자가 실제 api 요청 시 만료 시간이 5분 이하라면 그때 refresh 하자 즉 활성 사용자만 재발급 → 비활성 사용자 세션은 종료 동시 요청 문제 → Pending Queue 도입 다음 문제는 이거였다. 이거는 내가 면접 때 질문을 받고 대답을 못했던 그 문제이기도 한데 A API 요청 시 → 토큰 만료 → refresh 시작 그 사이에 B API 요청 → 또 토큰 만료 → refresh 또 호출 이러면 refresh api가 동시에 여러번 호출되며 경합이 발생한다. 그래서 필수적으로 refresh 중엔 다른 요청을 멈춰서 줄세우는 로직이 필요하였다. 그것이 바로 pending queue ~~내가 자료구조 수업에서 듣고 코테 풀때만 쓰던 큐를 진짜 써보는 날이 오다니~~ 요약하자면 refresh 중이면 → 요청을 queue 에 쌓아둠 refresh 끝나면 → queue에 있던 요청들을 새 토큰으로 재요청 이렇게 하면 동시 재발급 요청 문제는 해결된다. 로그아웃 중복 실행 문제 refresh 실패 시 로그아웃을 처리하도록 구성했지만, 동시에 여러 API가 실패할 경우 alert가 여러 번 표시되는 문제가 발생했다. 이를 방지하기 위해 isLoggingOut 상태를 추가하여 로그아웃이 한 번만 실행되도록 제어했다. 또한 이 상태값이 persist에 저장되지 않도록 설정하여 재로그인 이후 정상 동작하도록 수정했다. 최종 Axios 인터셉터 구조 정리 최종적으로 인터셉터 구조는 다음과 같이 정리되었다. 요청 시 Authorization 자동 주입 만료 5분 이하 → refresh 시도 refresh 중에는 pending queue 대기 refresh 실패 → 단일 로그아웃 처리 상태 관리는 zustand와 연계 정리하자면.. 이번에 인터셉터를 처음부터 설계하면서 느낀 점: 토큰 구조를 이해해야 한다 세션 만료 UX까지 고려해야 한다 동시 요청 문제까지 잡아야 한다 상태관리까지 연계해야 한다 인터셉터는 그냥 훅 두 개가 아니라 프로젝트의 전체 인증 흐름을 담당하는 중요한 부분임을 깨달았다. 그리고 무엇보다 절대… 생각 안하고 코드 복붙부터 시작하지 말자.

2025년 11월 20일 00:33

프론트엔드

BO에서 글을 수정해도 Web이 안 바뀌는 이유: 모노레포 환경 캐시 전략

BO에서 글 수정 시 Web에 미반영 문제 관리자 페이지(BO)에서 게시글을 수정하면 DB 및 BO에는 즉시 반영된다. 문제는 수정 직후 Web에서 해당 글을 확인하면, 내용이 이전 버전으로 계속 노출되었다. 관리자에서 수정이 완료되었는데 Web에는 반영되지 않는 상황이기 때문에, 운영 흐름상 수정 직후 화면에 변경 사항이 보이지 않는 것은 문제가 될 수밖에 없다. Web은 Next.js App Router + fetch 기반 SSR 구조로 구성되어 있었고, App Router의 fetch는 서버 레벨 캐싱을 기본으로 동작한다. 이는 일반적인 클라이언트 캐싱 라이브러리(예: React Query)와는 동작 방식이 다르다. App Router 환경에서 fetch는 기본적으로 force-cache로 동작한다. 단순 새로고침은 서버 재요청을 의미하지 않는다. 데이터는 빌드 시점 혹은 ISR 주기에 따라 캐시된다. 명시적으로 무효화하지 않는 한 캐시는 유지된다. 따라서 DB가 갱신되더라도, Web 서버의 캐시가 무효화되지 않으면 화면에는 반영되지 않는다. Web과 BO가 분리된 모노레포 구조 프로젝트는 pnpm 기반 모노레포로 구성되어 있다. 구조적으로 다음과 같은 특성을 가진다. web: SSR 기반 콘텐츠 렌더링 bo: CRUD 중심 관리자 기능 api: 데이터 처리 백엔드 이 세 애플리케이션은 같은 레포에 존재하지만, 런타임은 완전히 독립적으로 동작한다. 즉, 모노레포는 코드 레벨의 공유를 의미할 뿐, 런타임 공유를 의미하지 않는다. BO에서 게시글을 수정하면 DB는 갱신 되지만, Web 서버 내부 캐시는 별도의 프로세스로 유지된다. 따라서 BO에서 직접 revalidate를 호출하더라도 Web 서버의 캐시는 무효화되지 않는다. 설계한 캐시 무효화 전략 ① Web에 invalidation 전용 API route 생성 Web 서버 내부에서만 실행되는 캐시 무효화 엔드포인트를 별도로 구성하였다. 캐시 무효화는 반드시 Web 서버 내부에서 실행되도록 설계하였다. ② BO → API → Web Invalidation API 호출 게시글 생성/수정/삭제 성공 이후 BO가 호출한 백엔드 api는 Web의 invalidation API를 호출한다. 이를 통해 캐시 무효화 요청이 Web 서버로 전달된다. ③ Web 서버 내부에서 revalidateTag 실행 Web 서버 내부에서 revalidateTag를 호출하여 도메인 단위 캐시를 선택적응로 무효화한다. 게시글 상세는 도메인 단위 태그 + ID 단위 태그를 함께 사용했다. 전체 흐름은 다음과 같다. contracts 패키지에 revalidate 전략 포함하기 이 과정에서 contracts 패키지를 단순 타입 공유 계층으로 사용하지 않았다. 캐시 무효화 전략을 API 계약의 일부로 정의하였다. API contract는 다음과 같은 요소를 포함하였고, 캐시 전략을 위해서 여기에 revalidate 전략을 포함시켰다. HTTP Method Path Request Params / Body Response 타입 revalidate 즉, 특정 API가 호출되었을 때 어떤 캐시 태그가 무효화되어야 하는지까지 명시했다. 예시: UpdatePost Contract 설계 의도 이를 통해 다음과 같은 효과를 얻었다. BO와 Web이 동일한 무효화 규칙을 공유 도메인 단위 무효화 정책의 재사용 가능 캐시 전략을 명시적 설계 요소로 승격 결과적으로 contracts는 단순한 타입 패키지가 아니라 애플리케이션 간 계약을 정의하는 계층으로 확장되었다. 회고 이전에도 모노레포 구조를 구성해본 적은 있었지만, 두 개 이상의 애플리케이션을 동시에 개발하고 실제 데이터 흐름과 런타임 경계를 고려하며 설계한 것은 처음이었다. 특히 web, bo, api 세 애플리케이션과 contracts, ui 패키지가 유기적으로 연결된 구조에서 타입 공유만으로는 해결되지 않는 문제들이 드러났다. 같은 레포에 있다는 사실이 곧 실행 환경까지 공유한다는 의미는 아니었다. 런타임은 명확히 분리되어 있었고, 이번 캐시 이슈는 그 차이를 분명하게 보여준 사례였다. 또한 콘텐츠 운영 환경에서는 단순히 CRUD를 구현하는 것만으로는 충분하지 않다는 점을 알게 되었다. 데이터를 저장하는 것과 사용자 화면에 반영되는 것 사이에는 여러 계층이 존재한다. 이번 경험은 기능 구현을 넘어서 운영을 고려한 설계에 대해 고민하게 된 계기였다.

2026년 02월 19일 16:20

CS

[SQLD] GROUP BY

SQLD 2과목 - GROUP BY 절 GROUP BY 절 GROUP BY 절은 행을 특정 기준에 따라 그룹화하고, 각 그룹에 대해 집계 연산을 수행하기 위해 사용하는 구문이다. 주로 집계함수와 함께 사용되어 그룹별 요약 결과를 제공한다. GROUP BY 절에는 그룹 기준이 될 컬럼을 지정하며, 여러 개의 컬럼을 동시에 지정할 수 있다. 📌 주의 GROUP BY 연산에서 제외할 행이 있는경우 WHERE 절에서 미리 제외 GROUP BY 절에서 나열하지 않은 컬럼은 SELECT 에서 사용 불가 (단, 집계 함수에서 포함된 컬럼 예외) 집계 함수 여러 행의 값을 입력 받아 하나의 요약 값을 반환하는 다중 행 햄수 COUNT, SUM, AVG, MIN, MAX 등 하나의 인수만 허용 NULL 값은 연산에서 제외 ① COUNT 테이블의 행 수를 세는 함수 조건을 만족하는 행이 없는 경우에는 0ㅇ이 반환 NULL 값은 세지 않음 문자, 숫자, 날짜 타입 컬럼 중 하나의 인수만 전달 가능 COUNT(\)은 NULL 여부와 관계없이 항상 전체 행의 개수 반환 ② SUM 숫자 값의 총 합을 반환하는 함수 숫자 타입 컬럼만 인수로 전달 가능 조건을 만족하는 행이 없거나, 모든 값이 NULL 인 경우 NULL 반환 ③ AVG 평균값을 반환하는 집계 함수 숫자 타입 컬럼만 인수로 전달 가능 NULL 값을 제외한 값들에 대해서만 평균 계산 ④ MIN/MAX 각각 최솟값과 최댓값을 반환하는 집계 함수 날짜, 숫자, 문자 타입 모두에서 사용 가능 예시 예시1 부서별 직원수와 급여 총합 출력 👉 DEPTNO 값이 동일한 행들이 하나의 그룹으로 묶여, 각 그룹별로 COUNT, SUM 결과가 반환됨 예시2 가전제품의 카테고리와 브랜드별 평균 제품 가격 출력 👉 CATEGORY 와 BRAND 가 모두 동일한 경우 하나의 그룹으로 묶어, PRICE 에 대한 평균값을 출력함 예시3 성별과 학년별로 학생 수와 최대 키를 출력 👉 각 학년별로 남학생과 여학생을 각각 하나의 그룹으로 구분하여 키의 최대값을 출력함 예시4 GROUP BY 절의 잘못된 사용 예 👉 GROUP BY 절에 명시되지 않은 컬럼은 SELECT 절에 전달할 수 없음! HAVING 절 GROUP BY 로 그룹화 된 결과에 대해 조건을 설정할 때 사용 WHERE 절은 행 단위 조건을 적용하는 데 사용되지만 그룹화 된 결과에 대해 조건 설정이 불가하기 때문에 HAVING 절 사용** GROUP BY 결과에 대한 조건 설정 부서 번호별 급여 총합 급여 총합이 10000 이상인 그룹만 출력 👉 GROUP BY 결과에 대해 집계 함수 조건을 적용할 경우, WHERE 절이 아닌 HAVING 절에 조건 전달! 잘못된 조건 전달 👉 WHERE 절에서는 집계 함수(SUM)를 사용할 수 없음 집계 함수와 NULL 정리 ① NULL로만 구성된 행들에 대한 집계 결과 COUNT의 결과는 0 반환 그 외 집계 함수 결과는 NULL 반환 ② 조건에 맞는 데이터가 없는 경우에 대한 집계 결과 COUNT의 결과는 0 반환 그 외 집계 함수의 겨로가 NULL 반환 ③ 존재하지 않는 그룹에 대한 집계 결과 해당 그룹 자체가 생성되지 않으므로 결과는 공집합으로 반환

2026년 02월 15일 11:57

CS

[SQLD] WHERE 절

SQLD 2과목 - WHERE 절 WHERE 절 WHERE 절은 테이블에서 원하는 조건에 맞는 데이터만 조회할때 사용하는 절이다. 여러 조건을 지정할 수 있으며, AND와 OR 연산자로 조건을 연결할 수 있다. NULL 값 조회 시 IS NULL 또는 IS NOT NULL 연산자를 사용한다. ⭐️ 집계함수(SUM, AVG, COUNT 등)는 WHERE 절에서 사용할 수 없다. ⭐️ 컬럼 별칭은 WHERE 절에서 사용할 수 없다. 주의사항 문자 또는 날짜 상수 표현 시 홑따옴표 사용 Oracle에서는 문자 상수의 대소문자 구분 SQL Server에서는 기본 설정 기준으로 문자 상수의 대소문자 구분 ❌ 논리 연산자 NOT > AND > OR 순으로 연산 우선순위가 적용됨 ① AND 연산자 모든 조건을 동시에 만족하는 교집합 반환 ② OR 연산자 조건 중 하나라도 만족하면 포함하는 합집합 반환 ③ NOT 연산자 조건 결과의 반대 집합, 즉 여집합 반환 IN 연산자 여러 상수 중 하나와 일치하는 조건 전달 BETWEEN A AND B 연산자 A 이상이고 B 이하인 조건을 만족하는 대상을 선택 ❗️ A는 반드시 B보다 작아야 하며, 반대로 작성할 경우 공집합 LIKE 연산자 정확히 일치하지 않아도 되는 패턴 조건을 전달할 때 사용하는 연산자 와일드카드 문자(%, \)와 함께 사용 %: 자리수 제한 없이 모든 문자열 의미 : 한 글자를 의미, 하나당 한 자리의 모든 값 예시 ENAME LIKE 'S%' → 이름이 ‘S’로 시작하는 조건 ENAME LIKE '%S%' → 이름에 ‘S’가 포함된 조건 ENAME LIKE '%S' → 이름이 ‘S’로 끝나는 조건 ENAME LIKE '\S%' → 이름의 두 번째 글자가 ‘S’인 조건 ENAME LIKE '\\S\\' → 이름의 가운데 글자가 ‘S’이며, 전체 길이가 5 글자인 조건 ESCAPE CHARACTER % 또는 \를 와일드카드가 아닌 일반 문자로 조회하고 싶을 때 사용 특정 문자를 ESCAPE 문자로 지정하여 그 뒤에 오는 % 또는 \를 문자 그대로 인식하게 함 예시 WHERE ENAME LIKE 'A\\\%' ESCAPE '\\' → 이름이 ‘A\’로 시작하는 조건 (\_ 를 문자로 인식) ENAME LIKE '%!%%' ESCAPE '!' → 이름에 ‘%’ 문자가 포함된 조건

2026년 02월 13일 18:14

CS

[SQLD] 일반 함수

SQLD 2과목 – 일반함수 일반 함수 NVL NULL 값을 다른 값으로 치환 대상 값과 치환 값은 서로 동일한 데이터 타입 NVL2 대상 값이 NULL인지 여부에 따라 서로 다른 값 반환 특징 대상 값이 NULL이 아니면 첫번째 인수인 그외치환값 반환 대상 값이 NULL이면 두번째 인수인 널치환값 반환 반환되는 두 값은 서로 동일한 데이터 타입 COALESCE NULL 값을 치환 NVL과 달리 두 개 이상의 인수 지정 가능 특징 나열된 값 중에서 가장 먼저 등장하는 NULL이 아닌 값을 반환 모든 인수는 서로 호환 가능한 데이터 타입 NULLIF 두 값을 비교하여 동일한 경우 NULL 반환 특징 두 값이 서로 같으면 NULL, 다르면 첫번째 값 반환 ISNULL NULL값을 다른 값으로 치환 SQL Server에서만 제공 특징 대상 값이 NULL이면 치환값, NULL이 아니면 원래 값 반환 DECODE 조건에 따라 서로 다른 값을 반환 Oracle에서 제공 특징 대상값이 값1과 일치하면 결과1 반환 대상값이 값2와 일치하면 결과2 반환 기본값은 모든 조건에 일치하지 않을 경우 반환 (생략 시 NULL 반환) CASE문 조건에 따라 서로 다른 결과 반환 반드시 END 키워드로 마무리 조건과 그에 대응하는 치환값 여러 개 전달 가능 축약형 특징 ELSE 절 생략 시 NULL이 반환 모든 조건의 대상이 같고 항상 동등 비교 조건일 경우 축약형 사용 가능 축약형 문법에서는 WHERE과 THEN 사이에 상수 값만 배치 가능 축약형 문법에서 값과 결과의 데이터 타입이 일치하지 않으면 에러

2026년 02월 13일 16:59

CS

[SQLD] 변환 함수

SQLD 2과목 – 변환함수 데이터 타입을 다른 함수로 변환하는 함수 변환 함수 TONUMBER 문자 타입 데이터 → 숫자 타입 데이터 특징 숫자 형태의 문자와 숫자 데이터 간 연산시 묵시적 형 변환 발생해 연산 정상적 수행 묵시적 형변환 TOCHAR 숫자/날짜 타입 데이터 → 문자 타입 데이터 반환 결과는 항상 문자 특징 반환 대상이 숫자일 경우, 숫자 변환에 필요한 FORMAT 전달 반환 대상이 날짜일 경우, 날짜 변환에 필요한 FORMAT 전달 FORMAT 생략 시 단순히 문자 타입으로만 변환 📌 숫자 FORMAT 📌 날짜 FORMAT MONTH, DAY, DY, MON 등 문자 출력 포맷은 대소문자를 구분하므로 포맷 입력 형태에 따라 출력 결과 상이 포맷을 소문자로 입력하면 소문자로, 대문자로 입력하면 대문자로 출력 TODATE 문자 타입 데이터 → 날짜 타입 데이터 날짜처럼 생긴 문자열을 날짜 데이터로 인식시키기 위함 특징 날짜처럼 생긴 날짜문자열을 지정한 FORMAT에 맞게 해석하여 날짜 타입으로 변환 FORMAT 생략 시 세션의 기본 날짜 형식 기준으로 해석 두자리 연도 YY / RR 해석 TODATE 에서 지정되지 않은 날짜 요소 처리 방식 지정되지 않은 연도와 월은 현재 날짜 기준으로 설정 지정되지 않은 일, 시, 분, 초는 초기값으로 설정 날짜 연산 날짜 타입의 값과 숫자 간 더하기, 빼기 연산 가능 숫자 1은 하루를 의미 숫자 1/24는 한시간을 의미 날짜 - 날짜 연산의 결과는 두 날짜간의 일수 차이 숫자 반환

2026년 02월 13일 16:46