반응형

들어가며

Claude Code는 터미널에서 동작하는 에이전틱 코딩 도구로, 코드베이스를 이해하고 자연어 명령을 통해 파일 편집, 명령 실행, Git 워크플로우까지 수행합니다. 이 가이드는 Claude Code를 활용하여 프로덕션 수준의 고품질 애플리케이션을 체계적으로 개발하기 위한 절차와 베스트 프랙티스를 정리합니다.


Phase 1: 프로젝트 인프라 구축

고품질 결과물의 80%는 Claude Code에게 제공하는 컨텍스트 품질에 의해 결정됩니다. 코드 한 줄 작성하기 전에 프로젝트 인프라를 먼저 세팅하세요.

1.1 CLAUDE.md 설정 — 가장 중요한 단일 작업

CLAUDE.md는 Claude Code가 세션 시작 시 자동으로 읽는 프로젝트 메모리 파일입니다. 프로젝트의 컨벤션, 아키텍처, 기술 스택을 명시해두면 Claude가 일관된 코드를 생성합니다.

 
 
markdown
# CLAUDE.md

## 프로젝트 개요
- 프로젝트명: my-service
- 설명: 주문 관리 마이크로서비스

## 기술 스택
- Java 21 + Kotlin
- Spring Boot 3.4 (Spring MVC, Spring Data JPA)
- Gradle (Kotlin DSL)
- PostgreSQL, Redis
- RestClient (WebClient 대신 사용)

## 아키텍처 규칙
- Layered Architecture: Controller → Service → Repository
- DTO는 record(Java) 또는 data class(Kotlin) 사용
- Entity와 DTO를 반드시 분리
- 예외 처리는 @RestControllerAdvice 활용

## 코딩 컨벤션
- 패키지 구조: com.example.{도메인}.{layer}
- REST API는 /api/v1 prefix 사용
- 테스트는 반드시 작성 (JUnit 5 + MockK)
- 커밋 메시지: Conventional Commits 형식

## 빌드 & 실행
- 빌드: ./gradlew build
- 테스트: ./gradlew test
- 린트: ./gradlew ktlintCheck
- 실행: ./gradlew bootRun

## 절대 하지 말 것
- WebClient나 RestTemplate 사용 금지 (RestClient만 사용)
- var 남용 금지, 타입을 명시적으로 선언
- 테스트 없이 PR 생성 금지

 

CLAUDE.md 스코프 활용:

파일 위치적용 범위용도
~/.claude/CLAUDE.md 모든 프로젝트 개인 글로벌 설정
프로젝트루트/CLAUDE.md 해당 프로젝트 전체 팀 공유 컨벤션 (Git 커밋)
프로젝트루트/.claude/CLAUDE.md 해당 프로젝트 (로컬) 개인 로컬 설정 (.gitignore)

 

1.2 Rules 설정 — 주제별 모듈 지침

.claude/rules/ 디렉토리에 주제별 규칙 파일을 두면, Claude가 관련 작업 시 자동으로 참조합니다. frontmatter로 경로 스코핑도 가능합니다.

 
 
markdown
<!-- .claude/rules/api-design.md -->
---
globs: ["**/controller/**", "**/api/**"]
---

# API 설계 규칙
- ResponseEntity로 감싸서 반환
- 페이지네이션은 Spring Data의 Pageable 사용
- 400/404/500 에러 응답 형식 통일

1.3 Skills 설정 — 재사용 가능한 워크플로우

.claude/skills/ 디렉토리에 반복되는 작업 패턴을 정의하면 /skill-name으로 호출할 수 있습니다.

 
 
markdown
<!-- .claude/skills/new-api.md -->
# 새 API 엔드포인트 생성

## 절차
1. Controller 클래스 생성 (또는 기존에 추가)
2. Request/Response DTO 정의
3. Service 인터페이스 및 구현체 작성
4. Repository 생성 (필요 시)
5. 통합 테스트 작성
6. API 문서 업데이트

## 참고 패턴
- 기존 예시: src/main/kotlin/com/example/order/controller/OrderController.kt

1.4 Hooks 설정 — 자동 품질 게이트

Hooks는 Claude의 에이전틱 루프 바깥에서 결정론적으로 실행되는 스크립트입니다. 코드 변경 후 자동으로 린트/빌드를 실행하여 품질을 강제할 수 있습니다.

 
 
json
// .claude/settings.json
{
  "hooks": {
    "stop": [
      {
        "command": "./gradlew ktlintCheck --quiet",
        "description": "코드 스타일 자동 검증"
      }
    ]
  }
}

Phase 2: 계획 수립 (Plan Before Code)

리서치/계획과 구현을 반드시 분리하세요. Claude가 바로 코딩에 뛰어들게 하면, 잘못된 문제를 풀거나 아키텍처가 꼬이는 경우가 빈번합니다.

2.1 Plan Mode 활용

Shift+Tab 두 번으로 Plan Mode를 활성화합니다. 이 모드에서 Claude는 파일을 읽고 분석할 수 있지만, 어떤 파일도 수정하지 않습니다. 아키텍트 모드라고 생각하면 됩니다.

 
 
# Plan Mode에서의 프롬프트 예시
주문 취소 API를 구현해야 합니다.

요구사항:
- PUT /api/v1/orders/{id}/cancel
- 주문 상태가 PENDING일 때만 취소 가능
- 취소 시 재고 복원 필요
- 취소 사유(reason)를 기록

먼저 기존 OrderController와 OrderService를 분석하고,
구현 계획을 세워주세요. 아직 코드를 작성하지 마세요.

2.2 구조화된 프롬프트 작성 (CIF 원칙)

효과적인 프롬프트는 Context(맥락), Intent(의도), **Format(형식)**을 포함합니다.

 
 
# ❌ 모호한 프롬프트
폼 추가해줘

# ✅ 구조화된 프롬프트
[Context] OrderController.kt에 주문 수정 API가 필요합니다.
[Intent] 기존 createOrder 패턴을 따라서 updateOrder 엔드포인트를 구현해주세요.
[Format]
- PUT /api/v1/orders/{id}
- UpdateOrderRequest DTO 생성 (수량, 배송주소 변경 가능)
- 서비스 레이어에서 변경 감지 및 이벤트 발행
- 통합 테스트 포함
- 기존 OrderControllerTest.kt 패턴 참고

2.3 검증 기준을 먼저 제시

Claude에게 스스로 검증할 수 있는 수단을 제공하면 품질이 극적으로 향상됩니다. 테스트, 빌드 명령, 린트 등이 여기에 해당합니다.

 
 
구현 후 다음을 확인해주세요:
1. ./gradlew test 전체 통과
2. ./gradlew ktlintCheck 통과
3. 기존 테스트가 깨지지 않는지 확인

Phase 3: 구현

3.1 작업 단위 분할

복잡한 기능은 한 번에 구현하지 말고, 검증 가능한 단위로 나누세요.

 
 
# 주문 시스템 구현을 단계별로 나눈 예시

Step 1: "Order Entity와 Repository를 만들어주세요. 
         엔티티 생성 후 ./gradlew test로 확인."

/clear

Step 2: "OrderService를 구현해주세요. 
         주문 생성, 조회, 취소 로직 포함.
         각 메서드에 대한 단위 테스트 작성."

/clear

Step 3: "OrderController를 구현해주세요.
         기존 UserController.kt 패턴을 따라주세요.
         통합 테스트 작성 후 전체 테스트 실행."

3.2 /clear를 적극적으로 사용

새 작업을 시작할 때마다 /clear로 컨텍스트를 초기화하세요. 컨텍스트가 쌓이면 Claude의 성능이 저하되고, 토큰 소비가 증가하며, 이전 대화의 실수가 반복될 수 있습니다. 컨텍스트 열화(context degradation)는 Claude Code 사용 시 가장 흔한 실패 원인입니다.

 
 
# 이렇게 하지 마세요 (한 세션에서 모든 것)
"Entity 만들어줘" → "Service도 추가" → "아 그리고 Controller도" → "테스트도..."

# 이렇게 하세요 (작업 단위별 세션 분리)
[세션 1] Entity + Repository + 테스트 → /clear
[세션 2] Service + 테스트 → /clear  
[세션 3] Controller + 통합 테스트 → /clear

3.3 기존 코드를 예시로 제공

Claude Code는 프로젝트의 기존 패턴을 보여줄 때 가장 일관된 코드를 생성합니다.

 
 
ProductController를 새로 만들어주세요.
기존 OrderController.kt의 패턴(에러 핸들링, 응답 형식, 페이지네이션)을 
그대로 따라주세요.

3.4 병렬 세션 활용

독립적인 작업은 여러 Claude Code 세션을 동시에 실행하여 병렬로 처리할 수 있습니다. VS Code 확장을 사용하면 여러 패널에서 동시에 인스턴스를 실행할 수 있습니다.

Writer/Reviewer 패턴:

세션역할작업
세션 A Writer API 엔드포인트 구현
세션 B Reviewer 세션 A의 코드 리뷰 (편향 없이)

Test-First 패턴:

세션역할작업
세션 A 테스트 작성자 테스트 케이스 먼저 작성
세션 B 구현자 작성된 테스트를 통과하는 코드 구현

Phase 4: 품질 검증

4.1 자동 검증 루프

Claude Code가 스스로 검증하도록 구성하면, 사람이 피드백 루프에 개입할 필요가 줄어듭니다.

 
 
다음 순서로 진행해주세요:
1. OrderCancelService 구현
2. 단위 테스트 작성 및 실행
3. 테스트 실패 시 코드 수정 후 재실행
4. ./gradlew ktlintCheck로 스타일 검증
5. 전체 빌드 ./gradlew build 확인

4.2 MCP 통합으로 시각적 검증

MCP(Model Context Protocol) 서버를 연결하면 Claude가 브라우저, 콘솔 로그 등을 직접 확인할 수 있습니다.

  • Claude in Chrome: 실제 브라우저에서 UI를 열어 시각적 검증
  • Playwright MCP: 자동화된 브라우저 테스트 실행
  • Chrome DevTools MCP: 콘솔 로그와 네트워크 요청 확인

4.3 코드 리뷰 자동화

/install-github-app으로 Claude의 PR 자동 리뷰를 설정할 수 있습니다. 사람이 변수명을 지적하는 동안 Claude는 실제 로직 오류와 보안 이슈를 찾아냅니다.

 
 
yaml
# claude-code-review.yml
direct_prompt: |
  이 PR을 리뷰하고 버그와 보안 이슈만 보고해주세요.
  간결하게 작성하세요.
  코딩 스타일이나 변수명은 무시하세요.

Phase 5: Git 워크플로우 & 배포

5.1 커밋과 PR 자동화

Claude Code에게 Git 작업을 위임하면 시간을 절약하면서도 일관된 커밋 메시지를 유지할 수 있습니다.

 
 
# 변경사항을 Conventional Commits 형식으로 커밋
지금까지의 변경사항을 적절한 단위로 나눠서 커밋해주세요.
Conventional Commits 형식을 사용하세요.

# PR 생성
이 브랜치의 변경사항으로 PR을 만들어주세요.
- 제목: feat: 주문 취소 API 구현
- 본문에 변경 내용, 테스트 방법, 영향 범위를 포함

5.2 CI 파이프라인 연동

Claude Code를 CI/CD 파이프라인에서 비대화형(headless)으로 실행할 수 있습니다.

 
 
bash
# CI에서 자동 코드 수정 (린트 에러 등)
claude -p "린트 에러를 모두 수정해주세요" --allowedTools "Edit,Bash"

# 배치 작업
for issue in $ISSUES; do
  claude -p "이슈 $issue를 해결하고 PR을 생성해주세요"
done

핵심 원칙 요약

반드시 지켜야 할 것

  1. CLAUDE.md를 꼼꼼히 작성하세요 — 투자 대비 효과가 가장 큰 단일 작업입니다.
  2. 코딩 전에 반드시 계획하세요 — Plan Mode로 아키텍처를 먼저 확정하세요.
  3. 검증 수단을 제공하세요 — 테스트, 린트, 빌드 명령을 Claude에게 알려주세요.
  4. /clear를 습관화하세요 — 작업 전환 시 컨텍스트를 초기화하세요.
  5. 기존 패턴을 예시로 보여주세요 — 새 코드의 일관성이 크게 향상됩니다.
  6. 작업을 작게 나누세요 — 한 세션에 한 가지 작업만 집중하세요.

반드시 피해야 할 것

  1. Kitchen Sink 세션 — 한 세션에서 관련 없는 여러 작업을 섞지 마세요.
  2. 모호한 프롬프트 — "API 만들어줘" 대신 구체적인 스펙을 제공하세요.
  3. 검증 없는 수락 — Claude가 생성한 코드를 반드시 빌드/테스트하세요.
  4. MCP 서버 과다 연결 — 실제로 사용하는 것만 연결하세요 (4개 이내 권장).
  5. 코드 소유권 방기 — PR에 이름이 올라가는 것은 당신입니다. 최종 책임은 개발자에게 있습니다.

권장 워크플로우 치트시트

 
 
┌─────────────────────────────────────────────────┐
│  1. 인프라 세팅                                    │
│     CLAUDE.md 작성 → Rules 설정 → Hooks 구성       │
├─────────────────────────────────────────────────┤
│  2. 계획 수립 (Plan Mode)                          │
│     요구사항 분석 → 아키텍처 설계 → 계획 확인         │
├─────────────────────────────────────────────────┤
│  3. 단계별 구현                                    │
│     작업 분할 → 구현 → 검증 → /clear → 다음 작업    │
├─────────────────────────────────────────────────┤
│  4. 품질 검증                                      │
│     테스트 실행 → 린트 통과 → 빌드 확인              │
├─────────────────────────────────────────────────┤
│  5. Git 워크플로우                                  │
│     커밋 → PR 생성 → 코드 리뷰 → 머지               │
└─────────────────────────────────────────────────┘

참고 자료

반응형

반응형

네이버지도를 플러터에서 띄울 수 있는 플러그인입니다.

Plug-in which shows naver map on flutter project support Android and iOS.

Install 

해당 플러그인은 Naver Cloud PlatForm - map 에서 제공하는 map서비스를 Android와 iOS 환경에서 보여주는 플러그인입니다.

  • Naver cloud platform 에서 콘솔의 AI·Application Service > AI·NAVER API > Application에서 애플리케이션을 등록합니다.
  • 등록한 애플리케이션을 선택해 Client ID값을 확인하고 변경 화면에서 Maps가 선택되어 있는지 확인한다.

pubspec.yaml에 plug in dependencies에 작성

dependencies:
  naver_map_plugin: ^0.9.6

Warning 

  • 지도에서 제공하는 기본 컨트롤러가 잘 작동하지 않는 문제 (이유를 찾지 못하고 있음)
  • android는 현 위치 버튼만 정상 작동

NOTICE (Android) 

  • 한국어
    • 네이버에서 제공하는 SDK의 경우 안드로이드에서 지도를 표시하기 위해 기본값으로 GLSurfaceView를 사용한다. hot reload시에 naver map SDK의 binary에서 정확하지 않은 이유로 app crash가 발생한다. reload하지 않는 release version 에서는 성능이 더 좋은 GLSurfaceView를 사용하고, 아닌 경우 hot reload가 가능한 TextureView를 사용한다.
  • English
    • NaverMap SDK use GLSurfaceView as default to flush map view in Android. At hot reload, App crash occurs in binary file of naver map SDK caused by unclear reason. if you build released version, this plug-in use GLSurfaceView for better performance, otherwise TextureView is used to make hot-reload available.

ANDROID GETTING START 

AndroidManifest.xml에 지정

<manifest>
    <application>
        <meta-data
            android:name="com.naver.maps.map.CLIENT_ID"
            android:value="YOUR_CLIENT_ID_HERE" />
    </application>
</manifest>

naver map에서 현위치탐색 기능을 사용하기 위해서는 AndroidManifest.xml에서 권한을 명시한다.

<manifest>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
</manifest>

또한 android API level 23(M) 이상의 경우 동적 권한을 필요로 한다. 다음은 동적권한을 요청하는 예제 코드이다.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 0);
    }
}

iOS GETTING START 

대용량 파일을 받기 위해 git-lfs 설치가 필요합니다.

$ brew install git-lfs

그리고 git-lfs을 사용하기 위해 다음의 커맨드를 입력해주세요. lfs 사용 설정이 안될 경우 pod를 통한 dependency가 다운로드 되지않습니다.

$ git lfs install

info.plist에 지정

<dict>
  <key>NMFClientId</key>
  <string>YOUR_CLIENT_ID_HERE</string>
  <key>io.flutter.embedded_views_preview</key>
  <true/>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

naver map에서 현위치탐색 기능을 사용하기 위해서는 info.plist에서 권한을 명시한다.

<dict>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
	<string>[USAGE PERPOSE]</string>
	<key>NSLocationAlwaysUsageDescription</key>
	<string>[USAGE PERPOSE]</string>
	<key>NSLocationWhenInUseUsageDescription</key>
	<string>[USAGE PERPOSE]</string>
</dict>

이후 AppDelefate에서 위치 사용권한을 획득하는 예제.

if (CLLocationManager.locationServicesEnabled()) {
    switch CLLocationManager.authorizationStatus() {
    case .denied, .notDetermined, .restricted:
        self.manager.requestAlwaysAuthorization()
        break
    default:
        break
    }
}       

 

샘플 코드

import 'package:flutter/material.dart';

import 'package:naver_map_plugin_example/base_map.dart';
import 'package:naver_map_plugin_example/circle_map.dart';
import 'package:naver_map_plugin_example/padding_test.dart';
import 'package:naver_map_plugin_example/marker_map_page.dart';
import 'package:naver_map_plugin_example/path_map.dart';
import 'package:naver_map_plugin_example/polygon_map.dart';
import 'package:naver_map_plugin_example/text_field_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MainPage(),
    );
  }
}

class MainPage extends StatefulWidget {
  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  List<String> menuText = [
    '기본 지도 예제',
    '마커 예제',
    '패스 예제',
    '원형 오버레이 예제',
    '컨트롤러 테스트',
    '폴리곤 예제',
    'GLSurface Thread collision test',
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: menuText
              .map((text) => GestureDetector(
                    onTap: () => _onTapMenuItem(text),
                    child: Container(
                      margin: EdgeInsets.symmetric(vertical: 8),
                      padding: EdgeInsets.all(16),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(6),
                        border: Border.all(color: Colors.indigo),
                      ),
                      child: Text(
                        text,
                        style: TextStyle(
                          color: Colors.indigo,
                          fontSize: 12,
                          fontWeight: FontWeight.w600,
                        ),
                        textAlign: TextAlign.center,
                      ),
                    ),
                  ))
              .toList(),
        ),
      ),
    );
  }

  _onTapMenuItem(String text) {
    final index = menuText.indexOf(text);
    switch (index) {
      case 0:
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => BaseMapPage(),
            ));
        break;
      case 1:
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => MarkerMapPage(),
            ));
        break;
      case 2:
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => PathMapPage(),
            ));
        break;
      case 3:
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => CircleMapPage(),
            ));
        break;
      case 4:
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => PaddingTest(),
            ));
        break;
      case 5:
        Navigator.push(
            context,
            MaterialPageRoute(
              builder: (_) => PolygonMap(),
            ));
        break;
      case 6:
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (_) => TextFieldPage(),
          ));
    }
  }
}

 

출처: https://pub.dev/packages/naver_map_plugin

 

반응형

반응형
  1. Charles: 웹 개발자용 HTTP 디버깅 프로그램으로, Fiddler와 비슷한 기능을 제공합니다.
  2. Wireshark: 네트워크 패킷 분석 프로그램으로, Fiddler보다 복잡하지만 다양한 프로토콜을 분석할 수 있습니다.
  3. Postman: API 개발 및 테스트용 프로그램으로, Fiddler와 달리 API 대상을 직접 테스트할 수 있습니다.
  4. Burp Suite: 웹 애플리케이션 보안 테스트 프로그램으로, Fiddler와 비슷한 기능을 제공하며 보안 관련 기능도 제공합니다.

각 프로그램마다 다른 점이 있으니, 개인적인 요구에 맞는 프로그램을 선택하시면 됩니다.

반응형

반응형

Flutter SDK 다운로드


  • 명시적이고 간편한 경로에 압축을 해제합니다.(예: c:\src\flutter)

 

Flutter 기본 환경변수 PATH 설정


  • C:\src\flutter 기준

 

Windows 명령 프롬프트에서 flutter --version 실행해보자.

  • Flutter 3.0.1 표시되면 기본 PATH가 잘 설정된 것이다.

 

Flutter 개발 환경 준비(for Android)


Windows 명령 프롬프트에서 flutter doctor 실행해보자.

  • 모든 개발 환경 준비가 잘 되어 있다면 "No issues found!" 메시지를 확인 할 수 있다.
  • 준비되지 않은 체크 리스트는 해결 방법이 친절하게 제시되니 참고해서 해결하도록 하자

 

Flutter Doctor 이슈 해결방법


Android toolchain - Some Android licenses not accepted

  • Android 라이선스만 동의하면 쉽게 해결된다.
    • flutter doctor --android-licenses 그대로 타이핑하자.


 

반응형

반응형

 

 

하얀집가든 미나리삼겹살 : 네이버

육류,고기요리 · 매일 11:00 - 18:00, 야외 바베큐

m.place.naver.com

여름에는 수영장 펜션으로
푸짐하죠? ^^
묵은지와 삼겹살 그리고 버섯 꺄~
얼음컵 한가득 쉬원한 생맥주!
싱싱합니다.
먹고 또 먹고!
고기 먹고, 물놀이 하고, 고기 먹고, 물놀이하고~
자연 만끽~~~
야옹이들이 한가득~~

반응형

반응형

Concept의 개념

  • 일반적으로 함수 템플릿(또는 클래스 템플릿)은 모든 타입이 아니라 특정 조건을 만족하는 타입에 대해서 동작하게 됨
  • 타입이 가져야 하는 조건을 코드로 표현 한 것
  • 2000년 중반부터 이야기 되었으나 아직 표준화 되지 않음
  • C++20 표준에 추가될 예정
  • Concept 문법이 도입될 경우 템플릿 코드에 많은 변화가 있게됨
#include <iostream>
using namespace std;

struct Point
{
    int x, y;
};

// 실제 파라미터가 모든 타입이 아닌 연산자 < 가능한 타입에 대해서만 지원하는 템플릿임
template<typename T> T Min(T x, T y)
{
    return y < x ? y : x;
}

int main()
{
    Point p1, p2;
    Min(p1, p2);
}
#include <iostream>
using namespace std;

struct Point
{
    int x, y;
    bool operator<(const Point& p) { return true; }
};

// 1. concept 만들기
template<typename T>
concept bool LessThanComparable = requires(T a, T b)
{
    { a < b }-- > bool;
};

// 2. template 만들때 concept 표기
template<typename T> requires LessThanComparable<T>
T Min(T x, T y)
{
    return y < x ? y : x;
}

// 3. template 만들때 T대신 concept 사용
LessThanComparable Min(LessThanComparable x, LessThanComparable y)
{
    return y < x ? y : x;
}

int main()
{
    Point p1, p2;
    Min(p1, p2);
}
반응형

반응형

Typelist

  • 값이 아닌 타입의 리스트를 보관하는 데이터 타입

  • 템플릿 인자가 1개라도 Typelist를 활용하면 복수의 타입을 만들 수 있음

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, Nulltype>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, Nulltype>>>
#define TYPELIST_2(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, Nulltype>>>>

template<typename T> class xtuple {};

int main()
{
    xtuple<int> t1;

    Typelist<int, NullType> t1;
    Typelist<int, Typelist<int, NullType>> t2;
    Typelist<int, Typelist<int, Typelist<char, NullType>>> t3;
}

 

Typelist Length

  • 타입리스트의 요소 수 구하기

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>

// 1. 사용하는 모습을 보고 메인 템플릿 생성
template<typename T> struct Length;

// 2. 갯수를 구할 수 있도록 부분 특수화
template<typename T, typename U> struct Length<Typelist<T, U>>
{
    enum { value = Length<U>::value + 1 };
};

// 3. 재귀를 종료하기 위한 특수화
template<> struct Length<NullType>
{
    enum { value = 0 };
};

template<typename T> void test()
{
    cout << Length<T>::value << endl; // 4
}

int main()
{
    test<TYPELIST_4(int, int, double, float)>();
}

TypeAt

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>


// Typelist의 N번째 요소의 타입 구하기
// 메인 템플릿
template<typename T, int N> struct TypeAt
{
    //typedef ? type;
};

// 원하는 타입을 구할 수 있도록 부분 특수화
// N == 0
template<typename T, typename U> struct TypeAt<Typelist<T, U>, 0>
{
    typedef T type;
};

// 원하는 타입을 구할 수 있도록 부분 특수화
// N != 0
template<typename T, typename U, int N> struct TypeAt<Typelist<T, U>, N>
{
    typedef typename TypeAt<U, N-1>::type type;
};

template<typename T> void test()
{
    typename TypeAt<T, 0>::type i; // int
    cout << typeid(i).name() << endl;

    typename TypeAt<T, 1>::type c; // char
    cout << typeid(c).name() << endl;

    typename TypeAt<T, 2>::type d; // double
    cout << typeid(d).name() << endl;

    typename TypeAt<T, 3>::type l; // long
    cout << typeid(l).name() << endl;
}

int main()
{
    test<TYPELIST_4(int, char, double, long)>();
}

 

Append

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>

// Typelist 끝에 타입 추가하기
template<typename TL, typename T> struct Append;

// TL T
// 1. NullType, NullType => NullType
template<> struct Append<NullType, NullType>
{
    typedef NullType type;
};

// 2. NullType, 임의의 타입 => Typelist<임의의 타입, NullType>
template<typename T> struct Append<NullType, T>
{
    typedef Typelist<T, NullType> type;
};

// 3. NullType, Typelist<Head, Tail> = Typelist<Head, Tail>
template<typename Head, typename Tail> struct Append<NullType, Typelist<Head, Tail>>
{
    typedef Typelist<Head, Tail> type;
};

// 4. Typelist<Head, Tail>, NullType => Typelist<Head, Tail>
template<typename Head, typename Tail> struct Append<Typelist<Head, Tail>, NullType>
{
    typedef Typelist<Head, Tail> type;
};

// 5. Typelist<Head, Tail>, 임의의 타입 => Typelist<Head, Append<Tail, T>::type>
template<typename Head, typename Tail, typename T> struct Append<Typelist<Head, Tail>, T>
{
    typedef Typelist<Head, typename Append<Tail, T>::type> type;
};

template<typename T> void test()
{
    typename Append<T, int>::type t1;
    cout << typeid(t1).name() << endl; // int, char, double, int, NullType
}

int main()
{
    test<TYPELIST_3(int, char, double)>();
}

 

Typelist 예제

#include <iostream>
using namespace std;

// 값을 보관하지 않음
// 타입 2개를 보관함
template<typename T, typename U> struct Typelist
{
    typedef T Head;
    typedef U Tail;
};

struct NullType {};

// 매크로 도입
#define TYPELIST_1(T1) Typelist<T1, NullType>
#define TYPELIST_2(T1, T2) Typelist<T1, Typelist<T2, NullType>>
#define TYPELIST_3(T1, T2, T3) Typelist<T1, Typelist<T2, Typelist<T3, NullType>>>
#define TYPELIST_4(T1, T2, T3, T4) Typelist<T1, Typelist<T2, Typelist<T3, Typelist<T4, NullType>>>>

// Holder : 임의 타입의 data 1개 보관
template<typename T> struct Holder
{
    T value;
};

// GenScatterHierachy => MakeCode
template<typename T, template<typename> class Unit> 
class MakeCode : public Unit<T>
{
};

template<template<typename> class Unit> 
class MakeCode<NullType, Unit>
{
};

template<typename Head,             //            , Holder<double>,     empty 
    typename Tail,                  // Holder<int>, MakeCode<double, Unit>, MakeCode<NullType>
    template<typename> class Unit>  // MakeCode<int, Unit>, MakeCode<Typelist<double, NullType>, Unit
    
class MakeCode<Typelist<Head, Tail>, Unit> : public MakeCode<Head, Unit>, public MakeCode<Tail, Unit>
{
    // int value;
    // double value;
};

int main()
{
    // MakeCode의 1번째 인자가 Typelist일때
    MakeCode<TYPELIST_2(int, double), Holder> mc1;  // 기반 클래스 Holder<int>
                                // Holder<int>와 메모리 모양이 동일
    MakeCode<double, Holder> mc2; // Holder<double>
    MakeCode<NullType, Holder> mc3; // Empty
}
반응형

반응형

Member Detect IDioms

  • 클래스의 멤버 타입 존재 여부 확인

  • 클래스의 멤버 함수 존재 여부 확인

//컴파일 타임의 함수 시그니처에 대한 특성만 활용하는 특성을 이용하는 기법

// 함수 시그니처만 사용함으로 구현부가 없어도 괜찮음
char foo(int a);
short foo(double d);

int main()
{
    int n = 10;

    cout << sizeof(n) << endl; // 4
    //cout << sizeof(foo) << endl; // Error
    cout << sizeof(foo(3)) << endl; // 1
    cout << sizeof(foo(3.3)) << endl; // 2
}
#include <iostream>
#include <vector>
using namespace std;

// 멤버 타입을 가진 데이터용 템플릿(반환값이 char로 1바이트)
template<typename T> 
char check(typename T::value_type* a);

// 멤버 타입이 없는 데이터용 템플릿(반환값이 short로 2바이트)
template<typename T>
short check(...);

// 멤버 타입이 없는 데이터
struct NoValueType
{
};
// 멤버 타입이 있는 데이터
struct HasValueType
{
    typedef int value_type;
};

// 멤버 타입 존재를 여부 확인 함수
template<typename T>
struct has_value_type
{
    // 반환값이 char(1바이트)일때는 true, short(2바이트)일때는 false
    static constexpr bool value = (sizeof(check<T>(0)) == 1); 
};

int main()
{
    cout << has_value_type<HasValueType>::value << endl;
    cout << has_value_type<NoValueType>::value << endl;

    HasValueType t1;
    cout << sizeof(check<HasValueType>(0)) << endl;
    NoValueType t2;
    cout << sizeof(check<NoValueType>(0)) << endl;
}
#include <iostream>
#include <vector>
using namespace std;

// 멤버함수가 존재할 경우 
template<typename T> 
char check(decltype(T().resize(0))* a);

// 멤버함수가 존재하지 않을 경우
template<typename T>
short check(...);

// 멤버함수 존재 체크
template<typename T>
struct has_resize
{
    static constexpr bool value = (sizeof(check<T>(0)) == 1); 
};

int main()
{
    // vector는 resize가 있으므로 true 리턴
    cout << has_resize<vector<int>>::value << endl;
    
    // array는 resize가 없으므로 false 리턴
    cout << has_resize<array<int, 10>>::value << endl;
}
반응형

+ Recent posts