// 기본
@primary: #3498db;
.button { color: @primary; }
// 보간 (선택자, 속성, URL에서)
@component: 'button';
.@{component} { ... } // .button
background-@{property}: blue; // background-color: blue
url('@{path}/icon.svg')
// 변수를 이용한 변수
@theme: 'primary';
color: @@theme; // @primary 값
// 맵 (LESS 3.5+)
@colors: { primary: #3498db; danger: #e74c3c; };
color: @colors[primary];
// CSS 변수 조합
:root { --primary: @primary; }
.el { color: var(~'--@{prefix}-color'); }
~""이스케이프 (문자열 그대로 출력)@{}보간 (변수를 문자열로 치환)@@var변수를 이용한 변수 참조
gulp.src 파일 리스트 디버깅
// 1. gulp-debug (가장 간단)
gulp.src('src/**/*.js').pipe(require('gulp-debug')({ title: 'Files:' }))
// 2. through2 (상세 정보)
const through2 = require('through2')
gulp.src('src/**/*.js').pipe(
through2.obj((file, enc, cb) => {
console.log(file.relative)
cb(null, file)
})
)
// 3. glob 패턴 사전 확인
require('glob')('src/**/*.js', (err, files) => console.log(files))
// 조건부 디버깅: `NODE_ENV=debug gulp build`
const gulpif = require('gulp-if')
gulp.src('src/**/*.js').pipe(gulpif(process.env.NODE_ENV === 'debug', debug())) TypeScript Discriminated Union 문서화 딜레마
// 개발: 타입 안전, IDE 자동완성, 명확한 의도
type Button =
| { type: 'submit'; color: string }
| { type: 'reset'; text: string }
<!-- 문서화: 조건부 속성 설명 어려움, 테이블 복잡, 예시 다수 필요 -->
| 속성 | 타입 | 조건 |
| ----- | ------------------- | ----------------------- |
| type | 'submit' \| 'reset' | 필수 |
| color | string | type='submit'일 때 필수 |
| text | string | type='reset'일 때 필수 |
- 자세한 타입 정의는 TypeScript 정의 파일 참고
- typedoc, api-extractor로 자동 생성
onRest애니메이션이 물리적으로 정지했을 때 → UI 상태 업데이트용onResolvePromise가 resolve될 때 → 비동기 플로우 제어용
const [springs, api] = useSpring(() => ({
x: 0,
onRest: () => setStatus('stopped'), // UI 상태
onResolve: () => console.log('done'), // Promise 기반 로직
}))
// Promise 방식도 가능
await api.start({ x: 100 }) npm install 실패 시 플래그 선택
# 1. Node 버전 불일치 (engines 필드)
npm install --ignore-engines
# 2. peer dependencies 충돌 (가장 흔함)
npm install --legacy-peer-deps
# 3. 마지막 수단 (권장 안함)
npm install --force
NOTE
--legacy-peer-deps가 필요한 상황
{
"react": "^18.2.0",
// 라이브러리가 아직 React 18 공식 지원 안 하지만 실제로는 동작함.
"react-beautiful-dnd": "^13.1.1" // peer: react@^16.8 || ^17
}
# 프로젝트 전체 적용
echo "legacy-peer-deps=true" > .npmrc RTL에서 overflow scroll 상태 테스트
// scrollWidth > clientWidth면 가로 스크롤 필요
const isOverflowScrollable = (el) => ({
horizontal: el.scrollWidth > el.clientWidth,
vertical: el.scrollHeight > el.clientHeight,
})
test('overflow 확인', () => {
render(<ScrollableComponent parentWidth={300} childWidth={500} />)
const parent = screen.getByTestId('parent-container')
expect(isOverflowScrollable(parent).horizontal).toBe(true)
})
test('동적 크기 변경', () => {
const { rerender } = render(<Comp parentWidth={400} childWidth={300} />)
expect(isOverflowScrollable(screen.getByTestId('parent')).horizontal).toBe(
false
)
rerender(<Comp parentWidth={400} childWidth={600} />)
expect(isOverflowScrollable(screen.getByTestId('parent')).horizontal).toBe(
true
)
})
CAUTION
DOM 완전 렌더링 후 측정해야 정확. getBoundingClientRect()는 실제 크기 반환.
Node.js에서 윈도우즈 외부 경로 파일 읽기
const path = require('path')
const fs = require('fs').promises
// 슬래시 사용 (Node.js가 자동 변환)
// ('C:/Users/username/Documents/file.txt')
// 또는 백슬래시 이스케이프
// ('C:\\Users\\username\\Documents\\file.txt')
// 권장: path.join 사용 (크로스 플랫폼)
path.join(process.env.USERPROFILE, 'Documents', 'file.txt')
// path.resolve로 절대 경로 생성
path.resolve('../../Documents/file.txt')
CAUTION
사용자 입력으로 경로 받을 때는 path.normalize()로 디렉토리 트래버설 공격 방지.
Date.getDay() - 요일 반환 (0=일요일, 6=토요일)
new Date().getDay() // 0~6
new Date('2024-12-25').getDay() // 3 (수요일)
new Date(2024, 11, 25).getDay() // 월은 0부터 시작
const days = ['일', '월', '화', '수', '목', '금', '토']
days[new Date().getDay()] // 오늘 요일 검색어 필터링 UI → Debounce (300-500ms)
타이핑 완료 후 검색이 더 자연스러움. “자바스크립트” 입력 시 매 글자마다 검색하면 비효율적.
const handleSearch = debounce((term) => performSearch(term), 300)
input.addEventListener('input', (e) => handleSearch(e.target.value))
Throttle은 실시간 검색 결과를 보여주면서 요청 제한할 때 사용 (1초마다 최대 1회 등).
AsChild 패턴 (Radix UI)
컴포넌트 기능은 유지하면서 렌더링 요소를 변경:
<Button asChild>
<a href="/home">Link Button</a>
</Button>
핵심 구현 - Slot 컴포넌트:
const Slot = forwardRef(({ children, ...props }, ref) => {
if (!isValidElement(children)) return null
return cloneElement(children, {
...props,
...children.props,
ref: ref || children.ref,
})
})
자식 요소 타입에 따른 조건부 Props:
type ConditionalProps<T> = T extends ReactElement<any, 'a'>
? { href?: string; external?: boolean }
: T extends ReactElement<any, 'button'>
? { type?: 'button' | 'submit' }
: {}
// 사용
;<Anchor href="/home" external>
{' '}
{/* a 태그일 때만 href 허용 */}
<a>Link</a>
</Anchor>
활용: 디자인 시스템, 라우터 통합 (<Button asChild><NextLink /></Button>)