Local RAG MVP

퍼블리싱 가이드 챗봇

Markdown Search

이렇게 검색해 보세요

문장으로 길게 묻기보다 핵심 용어를 넣으면 더 잘 찾습니다. 예: 이미지 alt, 색 대비, 모달 접근성, form label, 배포 체크리스트

질문 예시 모음

접근성 질문

컴포넌트 질문

SCSS 질문

검사와 리뷰 질문

코드 검사

HTML 일부를 붙여넣거나 `.html` 파일을 선택해 접근성 기본 항목을 확인합니다.

배포 전 체크리스트

작업 흐름에 맞춰 기본 구조, 상호작용, 화면 품질, 검사 결과를 차례대로 확인합니다.

WORK TOOLS

바로 쓰는 작업 도구

01

페이지 유형별 체크리스트

작업 화면에 맞는 기본 검수 항목을 빠르게 생성합니다.

02

리뷰 메모

검토 내용을 전달하기 쉬운 요청 문장으로 정리합니다.

작업 시작 전

마크업 기본

폼과 입력

컴포넌트 상호작용

반응형과 화면 품질

검사 결과 재확인

기준 소스

자주 쓰는 HTML, CSS/SCSS, JS 기본 소스를 복사해서 시작점으로 사용할 수 있습니다.

HTML 기준

기본 HTML 구조

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>페이지 제목</title>
  </head>
  <body>
    <header>...</header>
    <main id="main">...</main>
    <footer>...</footer>
  </body>
</html>

접근 가능한 버튼

<button type="button">저장</button>

<button type="button" aria-label="닫기">
  <span aria-hidden="true">×</span>
</button>

폼 label 연결

<label for="email">이메일</label>
<input id="email" name="email" type="email" autocomplete="email">

<p id="email-error" class="error-message">이메일 형식을 확인해 주세요.</p>
<input aria-describedby="email-error">

이미지 alt

<img src="event-banner.jpg" alt="여름 할인 이벤트 안내">

<img src="decor-line.svg" alt="">

CSS/SCSS 기준

스킵 링크와 sr-only

<a class="skip-link" href="#main">본문 바로가기</a>

.skip-link {
  position: absolute;
  left: 16px;
  top: -48px;
}

.skip-link:focus {
  top: 16px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
}

컴포넌트 상태 클래스

.accordion {
  border: 1px solid #dfe3e6;
}

.accordion.is-open .accordion__panel {
  display: block;
}

.accordion__button:focus-visible {
  outline: 3px solid #1a73e8;
  outline-offset: 2px;
}

SCSS 변수와 breakpoint

$breakpoint-tablet: 768px;
$breakpoint-desktop: 1024px;
$color-focus: #1a73e8;

@mixin tablet {
  @media (min-width: $breakpoint-tablet) {
    @content;
  }
}

.card {
  padding: 16px;

  @include tablet {
    padding: 24px;
  }
}

SCSS 폴더 구조

scss/
  abstracts/
    _variables.scss
    _tokens.scss
    _mixins.scss
    _z-index.scss
  base/
    _font.scss
    _root.scss
    _reset.scss
    _accessibility.scss
  layout/
    _common-inner.scss
    _header.scss
    _footer.scss
  components/
    _button.scss
    _form.scss
    _modal.scss
    _popup.scss
    _table.scss
  templates/
    _template-base.scss
    _image-template.scss
    _text-template.scss
    _sub-page.scss
  sections/
    _visual.scss
    _info.scss
    _directions.scss
  editor/
    _design-edit-page.scss
    _selection.scss
    _design-util.scss
  style.scss

SCSS 공통 mixin

$breakpoint-pc: 1024px;

@mixin pc {
  @media screen and (min-width: $breakpoint-pc) {
    @content;
  }
}

@mixin public-mode {
  html:not(.design-edit-page) & {
    @content;
  }
}

@mixin public-pc {
  @include pc {
    html:not(.design-edit-page) & {
      @content;
    }
  }
}

@mixin editor-mode {
  html.design-edit-page & {
    @content;
  }
}

SCSS mixin 모음

@mixin font-style(
  $size,
  $weight: 400,
  $line-height: 1.5,
  $letter-spacing: 0,
  $color: null
) {
  font-size: $size;
  font-weight: $weight;
  line-height: $line-height;
  letter-spacing: $letter-spacing;

  @if $color != null {
    color: $color;
  }
}

@mixin position($position: absolute, $top: null, $right: null, $bottom: null, $left: null) {
  position: $position;

  @if $top != null { top: $top; }
  @if $right != null { right: $right; }
  @if $bottom != null { bottom: $bottom; }
  @if $left != null { left: $left; }
}

@mixin center-x($position: absolute) {
  position: $position;
  left: 50%;
  transform: translateX(-50%);
}

@mixin center-y($position: absolute) {
  position: $position;
  top: 50%;
  transform: translateY(-50%);
}

@mixin center($position: absolute) {
  position: $position;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

@mixin flex($direction: null, $justify: null, $align: null, $gap: null) {
  display: flex;

  @if $direction != null { flex-direction: $direction; }
  @if $justify != null { justify-content: $justify; }
  @if $align != null { align-items: $align; }
  @if $gap != null { gap: $gap; }
}

@mixin bg-image($url, $size: cover, $position: center) {
  background-image: url($url);
  background-repeat: no-repeat;
  background-position: $position;
  background-size: $size;
}

@mixin reset-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

@mixin ellipsis($lines: 1) {
  overflow: hidden;
  text-overflow: ellipsis;

  @if $lines == 1 {
    white-space: nowrap;
  } @else {
    display: -webkit-box;
    -webkit-line-clamp: $lines;
    -webkit-box-orient: vertical;
  }
}

@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}

SCSS 실무 mixin 예시

@use 'sass:math';

@function strip-unit($value) {
  @if type-of($value) == 'number' and not unitless($value) {
    @return math.div($value, $value * 0 + 1);
  }

  @return $value;
}

$breakpoints: (
  mobile: 480px,
  tablet: 768px,
  desktop: 1024px,
  wide: 1280px
);

@mixin respond-to($name) {
  $width: map-get($breakpoints, $name);

  @if $width == null {
    @error 'Unknown breakpoint: #{$name}';
  }

  @media (min-width: $width) {
    @content;
  }
}

@mixin fluid-type($min-size, $max-size, $min-vw: 360px, $max-vw: 1200px) {
  font-size: clamp(
    $min-size,
    calc(#{$min-size} + (#{strip-unit($max-size)} - #{strip-unit($min-size)}) * ((100vw - #{$min-vw}) / (#{strip-unit($max-vw)} - #{strip-unit($min-vw)}))),
    $max-size
  );
}

@mixin aspect-ratio-box($width, $height) {
  aspect-ratio: #{$width} / #{$height};

  @supports not (aspect-ratio: 1 / 1) {
    position: relative;

    &::before {
      content: '';
      display: block;
      padding-top: calc($height / $width * 100%);
    }

    > * {
      position: absolute;
      inset: 0;
    }
  }
}

@mixin absolute-fill {
  position: absolute;
  inset: 0;
}

@mixin hover-supported {
  @media (hover: hover) and (pointer: fine) {
    @content;
  }
}

@mixin reduced-motion {
  @media (prefers-reduced-motion: reduce) {
    @content;
  }
}

@mixin touch-target($size: 44px) {
  min-width: $size;
  min-height: $size;
}

SCSS 접근성 유틸

$color-focus: #1a73e8;

@mixin focus-ring {
  outline: 3px solid $color-focus;
  outline-offset: 2px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: 16px;
  top: -48px;
  z-index: 1000;

  &:focus {
    top: 16px;
    @include focus-ring;
  }
}

SCSS 컴포넌트 예시

.accordion {
  border: 1px solid $color-border;

  &__button {
    width: 100%;
    min-height: 44px;
    text-align: left;

    &:focus-visible {
      @include focus-ring;
    }
  }

  &__panel {
    padding: 16px;
  }

  &:not(.is-open) &__panel {
    display: none;
  }

  &.is-open &__button {
    font-weight: 700;
  }
}

SCSS style.scss 조립

@use 'abstracts/variables';
@use 'abstracts/tokens';
@use 'abstracts/mixins';

@use 'base/font';
@use 'base/root';
@use 'base/reset';
@use 'base/accessibility';

@use 'layout/common-inner';
@use 'layout/header';
@use 'layout/footer';

@use 'components/button';
@use 'components/form';
@use 'components/modal';
@use 'components/popup';

@use 'templates/template-base';
@use 'sections/visual';
@use 'sections/info';
@use 'editor/design-edit-page';

JS 기준

모달 포커스 복귀

const openButton = document.querySelector('[data-modal-open]');
const closeButton = document.querySelector('[data-modal-close]');
const modal = document.querySelector('[data-modal]');
let lastFocusedElement = null;

function openModal() {
  lastFocusedElement = document.activeElement;
  modal.hidden = false;
  closeButton.focus();
}

function closeModal() {
  modal.hidden = true;
  lastFocusedElement?.focus();
}

openButton.addEventListener('click', openModal);
closeButton.addEventListener('click', closeModal);

aria-expanded 토글

const button = document.querySelector('[data-toggle-button]');
const panel = document.querySelector('[data-toggle-panel]');

button.addEventListener('click', () => {
  const isOpen = button.getAttribute('aria-expanded') === 'true';
  button.setAttribute('aria-expanded', String(!isOpen));
  panel.hidden = isOpen;
});

사용법

실행 방법

PowerShell에서 프로젝트 폴더로 이동한 뒤 서버를 실행합니다.

cd "C:\Users\User\Documents\New project"
& 'C:\Users\User\.cache\codex-runtimes\codex-primary-runtime\dependencies\node\bin\node.exe' server.js

브라우저에서 http://localhost:3000을 엽니다. 서버를 끄려면 PowerShell에서 Ctrl + C를 누릅니다.

작업 흐름

  1. 작업 시작 전에는 체크리스트 탭에서 확인할 기준을 먼저 봅니다.
  2. 구현 중 헷갈리는 규칙은 가이드 질문 탭에서 짧은 핵심어로 검색합니다.
  3. 마크업을 작성한 뒤 코드 검사 탭에 HTML을 붙여넣거나 파일을 선택합니다.
  4. 검사 결과의 가이드 보기 버튼으로 관련 규칙을 다시 확인합니다.
  5. 마무리 전에 체크리스트 탭으로 돌아와 누락된 항목을 점검합니다.

바로 적용할 기준

참고자료를 열기 전에 회사 지침의 기본값으로 먼저 적용할 규칙입니다.

  • 접근성: WCAG 2.2 AA를 기본 목표로 둡니다.
  • ARIA: 기본 HTML로 해결되면 ARIA를 추가하지 않습니다.
  • 컴포넌트: 모달, 탭, 아코디언, 드롭다운은 WAI-ARIA APG의 키보드 패턴을 먼저 확인합니다.
  • HTML: `a`는 이동, `button`은 실행에 사용하고 클릭 가능한 `div`, `span`은 만들지 않습니다.
  • 폼: 폼 오류는 label, 설명, 오류 메시지를 함께 연결합니다.
  • SCSS: SCSS nesting은 3단계 이하로 제한합니다.
  • 리뷰: 자동 검사에서 오류가 없어도 색 대비, 포커스 이동, 키보드 조작은 수동 확인합니다.

필수 참고자료

회사 지침을 만들거나 퍼블리싱 중 판단이 필요할 때 먼저 확인할 자료입니다.

검색 팁

긴 문장보다 핵심어가 잘 잡힙니다.

  • 이미지 alt
  • 색 대비
  • 모달 접근성
  • form label
  • 폼 오류 메시지
  • button link 구분
  • 배포 전 접근성 체크리스트

챗봇 질문 예시

아래처럼 핵심어와 목적을 함께 넣으면 답을 더 잘 찾습니다.

  • SCSS 파일은 어떻게 나누면 좋아?
  • 모달 접근성에서 확인할 것은 뭐야?
  • 드롭다운은 어떻게 마크업해?
  • 검사 결과는 어떻게 다시 확인해?
  • 회사 CSS 분석 내용 보여줘

검색이 안 될 때

질문을 더 짧게 바꾸거나, 문서에 해당 내용이 있는지 확인합니다. 표현만 다른 경우에는 src/search.js의 동의어 목록에 추가하면 됩니다.

문서 추가 위치

챗봇 답변은 docs/guide/ 안의 Markdown 문서에서 나옵니다. 프로젝트 규칙이 생기면 이 폴더의 문서를 먼저 수정하세요.