백엔드 개발자와의 첫 회의 체크리스트

odada·2025년 2월 6일

백엔드 개발자와의 첫 회의 체크리스트

백엔드 개발자 첫 회의 체크리스트

1. 인증/보안 관련

항목체크답변 기록추가 질문
JWT 토큰 형태 (access/refresh)
토큰 만료 시간
토큰 갱신 방식
CORS 허용 도메인
인증 헤더 형식

2. API 아키텍처

항목체크답변 기록추가 질문
서버 개발 스택
API 문서 제공 방식
API 기본 URL 구조
API 버전 관리 방식
REST API vs GraphQL

3. 데이터 통신

항목체크답변 기록추가 질문
실시간 데이터 처리 방식
웨이퍼 데이터 구조
페이지네이션 방식
에러 응답 구조
대용량 데이터 처리 방식

4. 개발 환경

항목체크답변 기록추가 질문
개발 서버 정보
스테이징 서버 정보
테스트 계정/데이터
CI/CD 파이프라인

5. 성능 관련

항목체크답변 기록추가 질문
API 응답시간 기준
데이터 캐싱 전략
동시접속 처리 방안
대용량 처리 최적화

6. 보안

항목체크답변 기록추가 질문
권한 체계
민감 데이터 처리
API 보안 헤더
데이터 암호화 방식

7. 협업 방식

항목체크답변 기록추가 질문
코드 저장소 정보
API 변경 공유 방식
이슈 트래킹 도구
커뮤니케이션 채널
정기 회의 일정

8. 테스트

항목체크답변 기록추가 질문
API 테스트 환경
테스트 데이터 제공
통합 테스트 방식
QA 프로세스

9. 모니터링/로깅

항목체크답변 기록추가 질문
에러 모니터링 도구
로그 수집 방식
알림 설정
성능 모니터링

메모

회의 중 추가로 논의된 중요 사항들을 기록하세요:

후속 조치

회의 후 필요한 후속 조치들을 기록하세요:

인증 시스템

1. JWT 토큰 구조

질문: "로그인 성공시 어떤 형태의 토큰을 받게 되나요?"

예상 답변:

// 패턴 1: accessToken + refreshToken
{
  "accessToken": "eyJhbGciOiJIUzI1...",
  "refreshToken": "eyJhbGciOiJIUzI1..."
}

// 패턴 2: accessToken만 사용
{
  "accessToken": "eyJhbGciOiJIUzI1..."
}

프론트엔드 적용:

// src/services/auth.ts
interface LoginResponse {
  accessToken: string;
  refreshToken?: string;
}

const login = async (username: string, password: string): Promise<LoginResponse> => {
  const response = await axios.post('/api/auth/login', { username, password });
  
  // 토큰 저장
  localStorage.setItem('accessToken', response.data.accessToken);
  if (response.data.refreshToken) {
    localStorage.setItem('refreshToken', response.data.refreshToken);
  }
  
  return response.data;
};

2. 토큰 갱신 방식

질문: "토큰이 만료되면 어떻게 갱신하나요?"

예상 답변:

// 패턴 1: refresh token 사용
"POST /api/auth/refresh로 refreshToken을 보내주시면 새로운 accessToken을 발급해드립니다."

// 패턴 2: 자동 갱신
"토큰 만료 10분 전에 자동으로 새로운 토큰을 발급합니다."

// 패턴 3: 재로그인
"토큰 만료시 다시 로그인해야 합니다."

프론트엔드 적용:

// src/services/axios.ts
import axios from 'axios';

// axios 인터셉터 설정
axios.interceptors.response.use(
  response => response,
  async error => {
    if (error.response?.status === 401) {
      try {
        const refreshToken = localStorage.getItem('refreshToken');
        const response = await axios.post('/api/auth/refresh', { refreshToken });
        localStorage.setItem('accessToken', response.data.accessToken);
        
        // 실패했던 요청 재시도
        return axios(error.config);
      } catch {
        // 갱신 실패시 로그인 페이지로
        window.location.href = '/login';
      }
    }
    return Promise.reject(error);
  }
);

API 구조

1. API 문서화

질문: "API 문서는 어떤 형태로 제공되나요?"

예상 답변:

// 패턴 1: Swagger
"Swagger UI를 통해 제공됩니다. 주소는 http://api-server/swagger-ui.html 입니다."

// 패턴 2: Postman
"Postman 컬렉션 파일을 공유드리겠습니다."

// 패턴 3: 정적 문서
"Git 저장소의 /docs 폴더에 마크다운 형식으로 제공됩니다."

프론트엔드 적용:

// src/api/types.ts - Swagger에서 생성된 타입 정의
interface WaferData {
  id: string;
  timestamp: string;
  measurements: {
    temperature: number;
    pressure: number;
  };
}

// src/api/wafers.ts
const getWaferData = async (id: string): Promise<WaferData> => {
  const response = await axios.get(`/api/wafers/${id}`);
  return response.data;
};

2. API 엔드포인트 구조

질문: "API 엔드포인트 구조는 어떻게 되나요?"

예상 답변:

// 패턴 1: REST 기반
"/api/v1/wafers - 웨이퍼 관련 엔드포인트
/api/v1/measurements - 계측 데이터 관련 엔드포인트"

// 패턴 2: 기능 기반
"/api/wafer-management/measurements
/api/user-management/permissions"

프론트엔드 적용:

// src/api/constants.ts
export const API_ENDPOINTS = {
  WAFERS: '/api/v1/wafers',
  MEASUREMENTS: '/api/v1/measurements',
  AUTH: '/api/v1/auth'
} as const;

// src/api/wafers.ts
import { API_ENDPOINTS } from './constants';

export const getWaferList = () => 
  axios.get(API_ENDPOINTS.WAFERS);

실시간 데이터 처리

1. WebSocket 연결

질문: "실시간 데이터는 어떤 방식으로 받게 되나요?"

예상 답변:

// 패턴 1: WebSocket
"ws://api-server/wafers/{waferId}/measurements 로 연결하시면 됩니다."

// 패턴 2: Server-Sent Events
"GET /api/wafers/stream 엔드포인트로 SSE 연결을 하시면 됩니다."

프론트엔드 적용:

// src/services/websocket.ts
class WaferWebSocket {
  private ws: WebSocket;
  
  constructor(waferId: string) {
    this.ws = new WebSocket(`ws://api-server/wafers/${waferId}/measurements`);
    
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      // 데이터 처리
    };
    
    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
    };
  }
  
  disconnect() {
    this.ws.close();
  }
}

에러 처리

1. 에러 응답 구조

질문: "API 에러는 어떤 형식으로 반환되나요?"

예상 답변:

// 패턴 1: 표준 에러 객체
{
  "code": "WAFER_NOT_FOUND",
  "message": "웨이퍼를 찾을 수 없습니다.",
  "status": 404
}

// 패턴 2: 상세 에러 정보
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "유효하지 않은 입력입니다.",
    "details": [
      {
        "field": "temperature",
        "message": "온도는 0~100 사이여야 합니다."
      }
    ]
  }
}

프론트엔드 적용:

// src/types/api.ts
interface ApiError {
  code: string;
  message: string;
  status: number;
  details?: Array<{
    field: string;
    message: string;
  }>;
}

// src/services/error-handler.ts
const handleApiError = (error: unknown) => {
  if (axios.isAxiosError(error) && error.response) {
    const apiError = error.response.data as ApiError;
    
    switch (apiError.code) {
      case 'VALIDATION_ERROR':
        toast.error('입력값을 확인해주세요');
        break;
      case 'UNAUTHORIZED':
        window.location.href = '/login';
        break;
      default:
        toast.error(apiError.message);
    }
  } else {
    toast.error('알 수 없는 에러가 발생했습니다.');
  }
};

이러한 내용들은 프로젝트 초기 구조를 잡는데 매우 중요합니다. 특히 인증 시스템과 실시간 데이터 처리 방식은 전체 애플리케이션 아키텍처에 큰 영향을 미치므로, 초기에 명확히 해두는 것이 좋습니다.

성능 관련 체크리스트

1. API 응답 시간

질문

  • "API 응답 시간의 기준이 있나요?"
  • "데이터 크기별로 예상 응답 시간을 알 수 있을까요?"

예상 답변

// 패턴 1: 일반적인 기준
"일반 API: 1초 이내
대용량 데이터: 3초 이내
실시간 데이터: 100ms 이내"

// 패턴 2: 상황별 기준
"- 웨이퍼 목록 조회: 500ms 이내
- 상세 데이터 조회: 1초 이내
- 대시보드 데이터: 2초 이내
- 보고서 생성: 5초 이내"

프론트엔드 적용

// src/services/api-client.ts
import axios from 'axios';

const API_TIMEOUT = {
  DEFAULT: 5000,    // 5초
  REPORT: 10000,    // 10초
  REALTIME: 1000    // 1초
};

// API 클라이언트 설정
const createApiClient = (type: 'DEFAULT' | 'REPORT' | 'REALTIME') => {
  return axios.create({
    timeout: API_TIMEOUT[type],
    headers: {
      'Content-Type': 'application/json',
    }
  });
};

// 타임아웃 처리
const handleTimeout = async (promise: Promise<any>) => {
  try {
    const result = await promise;
    return result;
  } catch (error) {
    if (axios.isAxiosError(error) && error.code === 'ECONNABORTED') {
      showToast('요청 시간이 초과되었습니다. 다시 시도해주세요.');
    }
    throw error;
  }
};

2. 대용량 데이터 처리

질문

  • "한 번에 처리 가능한 최대 데이터 크기는 어느 정도인가요?"
  • "대용량 데이터는 어떤 방식으로 전달받나요?"

예상 답변

// 패턴 1: 페이지네이션
"한 페이지당 100건씩 페이지네이션으로 제공
정렬/필터링은 서버에서 처리"

// 패턴 2: 청크 단위 전송
"최대 1MB 단위로 청크 분할 전송
/api/data?chunk=1 형식으로 요청"

// 패턴 3: 스트리밍
"대용량 데이터는 WebSocket을 통해 스트리밍 방식으로 전송"

프론트엔드 적용

// src/hooks/usePagination.ts
interface PaginationParams {
  page: number;
  pageSize: number;
  filters?: Record<string, any>;
}

const usePagination = <T>(
  fetchFn: (params: PaginationParams) => Promise<T[]>
) => {
  const [data, setData] = useState<T[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const fetchPage = async (params: PaginationParams) => {
    setLoading(true);
    try {
      const result = await fetchFn(params);
      setData(prev => [...prev, ...result]);
    } catch (err) {
      setError(err as Error);
    } finally {
      setLoading(false);
    }
  };

  return { data, loading, error, fetchPage };
};

// 사용 예시
const WaferList = () => {
  const { data, loading, fetchPage } = usePagination(fetchWafers);

  // 무한 스크롤 구현
  const handleScroll = useCallback((entries: IntersectionObserverEntry[]) => {
    const target = entries[0];
    if (target.isIntersecting && !loading) {
      fetchPage({ page: currentPage, pageSize: 100 });
    }
  }, [loading, currentPage]);

  return (
    <VirtualizedList
      data={data}
      renderItem={(item) => <WaferItem data={item} />}
      onEndReached={handleScroll}
    />
  );
};

3. 캐싱 전략

질문

  • "어떤 데이터를 캐싱해야 하나요?"
  • "캐시 유효 시간은 어떻게 되나요?"

예상 답변

// 패턴 1: 시간 기반 캐싱
"- 마스터 데이터: 1시간
- 사용자 정보: 30- 실시간 데이터: 캐싱하지 않음"

// 패턴 2: 이벤트 기반 캐싱
"데이터 업데이트 이벤트 발생 시 캐시 무효화"

프론트엔드 적용

// src/services/cache.ts
import { QueryClient } from '@tanstack/react-query';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      // 기본 캐시 설정
      staleTime: 5 * 60 * 1000, // 5분
      cacheTime: 30 * 60 * 1000, // 30분
    },
  },
});

// 캐시 설정
const CACHE_CONFIG = {
  masterData: {
    staleTime: 60 * 60 * 1000, // 1시간
    cacheTime: 2 * 60 * 60 * 1000, // 2시간
  },
  userInfo: {
    staleTime: 30 * 60 * 1000, // 30분
    cacheTime: 60 * 60 * 1000, // 1시간
  },
  realtime: {
    staleTime: 0, // 항상 최신 데이터 fetch
    cacheTime: 0,
  },
};

// 캐시 무효화 함수
const invalidateCache = async (key: string) => {
  await queryClient.invalidateQueries({ queryKey: [key] });
};

// WebSocket 이벤트로 캐시 무효화
socket.on('dataUpdated', (event) => {
  invalidateCache(event.dataType);
});

4. 최적화 전략

질문

  • "브라우저에서 처리해야 할 데이터의 권장 크기가 있나요?"
  • "서버에서 어떤 최적화를 제공하나요?"

예상 답변

// 패턴 1: 데이터 최적화
"- 필요한 필드만 선택적으로 전송
- 응답 데이터 압축 사용
- 이미지/파일은 CDN 사용"

// 패턴 2: 처리 최적화
"- 무거운 연산은 서버에서 처리
- 클라이언트는 표시용 데이터만 처리
- 실시간 데이터는 변경된 부분만 전송"

프론트엔드 적용

// src/hooks/useOptimizedData.ts
interface OptimizationOptions {
  debounceMs?: number;
  batchSize?: number;
  virtualizeThreshold?: number;
}

const useOptimizedData = <T>(
  data: T[],
  options: OptimizationOptions = {}
) => {
  // 데이터가 많은 경우 가상화 적용
  const shouldVirtualize = data.length > (options.virtualizeThreshold ?? 1000);

  // 실시간 업데이트 최적화
  const debouncedUpdate = useMemo(
    () => debounce((newData: T[]) => {
      setProcessedData(newData);
    }, options.debounceMs ?? 300),
    [options.debounceMs]
  );

  // 대용량 데이터 처리 최적화
  const processInBatches = useCallback((items: T[]) => {
    const batchSize = options.batchSize ?? 100;
    let processed = 0;

    const processBatch = () => {
      const batch = items.slice(processed, processed + batchSize);
      // 배치 처리 로직
      processed += batchSize;

      if (processed < items.length) {
        requestAnimationFrame(processBatch);
      }
    };

    processBatch();
  }, [options.batchSize]);

  return {
    processedData,
    shouldVirtualize,
    updateData: debouncedUpdate,
    processInBatches
  };
};

이러한 성능 최적화 전략들은 프로젝트 초기에 백엔드 팀과 합의하고, 프론트엔드 아키텍처에 반영하는 것이 중요합니다. 특히 실시간 데이터를 다루는 시스템에서는 데이터 처리 방식과 캐싱 전략이 매우 중요한 요소가 됩니다.

보안 관련 체크리스트

1. 권한 체계

질문

  • "사용자 권한은 어떤 체계로 관리되나요?"
  • "각 API 엔드포인트별 필요한 권한을 어떻게 알 수 있나요?"

예상 답변

// 패턴 1: 역할 기반 권한
"ADMIN, MANAGER, OPERATOR 등 역할별로 권한 구분
각 API 문서에 필요한 역할이 명시되어 있음"

// 패턴 2: 리소스 기반 권한
"웨이퍼 조회, 수정, 삭제 등 작업별 권한 부여
특정 설비나 공정에 대한 접근 권한 별도 관리"

프론트엔드 적용

// src/types/auth.ts
type UserRole = 'ADMIN' | 'MANAGER' | 'OPERATOR';

interface Permission {
  resource: string;
  actions: Array<'read' | 'write' | 'delete'>;
}

// src/hooks/usePermission.ts
const usePermission = () => {
  const user = useSelector(selectUser);
  
  const checkPermission = useCallback((
    requiredRole: UserRole | UserRole[]
  ) => {
    const roles = Array.isArray(requiredRole) ? requiredRole : [requiredRole];
    return roles.includes(user.role);
  }, [user.role]);

  return { checkPermission };
};

// 사용 예시
const WaferControl = () => {
  const { checkPermission } = usePermission();

  return (
    <div>
      {checkPermission('OPERATOR') && (
        <button onClick={handleProcess}>공정 시작</button>
      )}
      {checkPermission(['ADMIN', 'MANAGER']) && (
        <button onClick={handleDelete}>데이터 삭제</button>
      )}
    </div>
  );
};

2. 민감 데이터 처리

질문

  • "어떤 데이터가 민감 정보로 분류되나요?"
  • "민감 데이터는 어떻게 마스킹 처리해야 하나요?"

예상 답변

// 패턴 1: 데이터 분류
"- 사용자 개인정보: 이름, 전화번호 등
- 설비 정보: 일련번호, 보정값 등
- 공정 데이터: 특정 파라미터값"

// 패턴 2: 마스킹 규칙
"- 이름:*동 형식으로 표시
- 전화번호: 010-****-5678 형식
- 민감 수치: 권한에 따라 표시 여부 결정"

프론트엔드 적용

// src/utils/security.ts
const maskingRules = {
  name: (value: string) => {
    if (!value) return '';
    return value.replace(/(?<=.)./g, '*');
  },
  phone: (value: string) => {
    if (!value) return '';
    return value.replace(/(\d{3})(\d{4})(\d{4})/, '$1-****-$3');
  },
  number: (value: number, userRole: UserRole) => {
    if (userRole === 'ADMIN') return value;
    return '**.**';
  }
};

// src/components/SecureData.tsx
interface SecureDataProps {
  type: keyof typeof maskingRules;
  value: string | number;
}

const SecureData = ({ type, value }: SecureDataProps) => {
  const { role } = useSelector(selectUser);
  const maskedValue = maskingRules[type](value, role);
  
  return <span>{maskedValue}</span>;
};

// 사용 예시
const UserInfo = ({ user }) => (
  <div>
    <SecureData type="name" value={user.name} />
    <SecureData type="phone" value={user.phone} />
  </div>
);

3. XSS 방지

질문

  • "사용자 입력값 처리시 특별히 고려할 사항이 있나요?"
  • "API 응답의 데이터는 어떻게 sanitize 해야 하나요?"

예상 답변

// 패턴 1: 입력값 처리
"모든 사용자 입력은 HTML 인코딩 필요
특수문자는 이스케이프 처리"

// 패턴 2: 출력값 처리
"API 응답의 텍스트 데이터는 항상 sanitize 처리
스크립트 태그 등은 제거 또는 이스케이프"

프론트엔드 적용

// src/utils/security.ts
import DOMPurify from 'dompurify';

const sanitizeInput = (value: string): string => {
  return DOMPurify.sanitize(value, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong'],
    ALLOWED_ATTR: []
  });
};

// src/hooks/useSafeForm.ts
const useSafeForm = <T extends Record<string, any>>(
  onSubmit: (data: T) => void
) => {
  const handleSubmit = useCallback((data: T) => {
    const sanitizedData = Object.entries(data).reduce(
      (acc, [key, value]) => ({
        ...acc,
        [key]: typeof value === 'string' ? sanitizeInput(value) : value
      }),
      {} as T
    );
    
    onSubmit(sanitizedData);
  }, [onSubmit]);

  return { handleSubmit };
};

// 사용 예시
const CommentForm = () => {
  const { handleSubmit } = useSafeForm<{ content: string }>(
    async (data) => {
      await submitComment(data);
    }
  );

  return (
    <form onSubmit={handleSubmit}>
      <textarea name="content" />
      <button type="submit">작성</button>
    </form>
  );
};

4. API 보안

질문

  • "API 호출시 어떤 보안 헤더를 포함해야 하나요?"
  • "민감한 API의 추가 인증 방식이 있나요?"

예상 답변

// 패턴 1: 기본 보안 헤더
"- Authorization: Bearer JWT
- X-CSRF-Token: 필수
- Content-Security-Policy 준수"

// 패턴 2: 추가 인증
"민감 작업은 2인증(OTP) 필요
특정 IP에서만 접근 가능한 API 존재"

프론트엔드 적용

// src/services/api-security.ts
import axios from 'axios';

const secureApiClient = axios.create({
  baseURL: '/api',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': getCsrfToken()
  }
});

// 보안 헤더 인터셉터
secureApiClient.interceptors.request.use((config) => {
  // JWT 토큰
  const token = localStorage.getItem('accessToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  
  // CSRF 토큰
  const csrfToken = getCsrfToken();
  if (csrfToken) {
    config.headers['X-CSRF-Token'] = csrfToken;
  }
  
  return config;
});

// 민감 API 호출시 2차 인증
const callSecureApi = async (
  endpoint: string, 
  data: any, 
  requireOtp: boolean = false
) => {
  if (requireOtp) {
    const otpToken = await requestOtpVerification();
    return secureApiClient.post(endpoint, data, {
      headers: {
        'X-OTP-Token': otpToken
      }
    });
  }
  
  return secureApiClient.post(endpoint, data);
};

보안과 관련된 설정은 프로젝트 초기에 명확히 해두어야 하며, 특히 민감한 데이터를 다루는 시스템에서는 더욱 중요합니다. 백엔드 팀과의 긴밀한 협의를 통해 보안 정책을 수립하고, 프론트엔드에서 이를 철저히 구현해야 합니다.

협업 관련 체크리스트

1. API 변경 관리

질문

  • "API가 변경될 때 어떻게 공유되나요?"
  • "API 변경 시 프론트엔드에서 대응해야 할 프로세스가 있나요?"

예상 답변

// 패턴 1: 문서 기반 관리
"Swagger 문서가 자동으로 업데이트됩니다.
주요 변경사항은 Slack 채널에 공지됩니다."

// 패턴 2: 버전 관리
"- API 버전이 변경됨을 사전에 공지
- 이전 버전은 1개월간 유지
- release notes를 통해 변경사항 공유"

프론트엔드 적용

// src/config/api-version.ts
const API_VERSION = {
  CURRENT: 'v1',
  DEPRECATED: [], // 곧 제거될 버전들
  SUPPORTED: ['v1', 'v2-beta'] // 지원되는 버전들
};

// src/services/api-client.ts
const createApiClient = (version = API_VERSION.CURRENT) => {
  return axios.create({
    baseURL: `/api/${version}`,
    headers: {
      'Api-Version': version
    }
  });
};

// 변경된 API 마이그레이션 예시
const migrateToNewApi = async () => {
  try {
    // 신규 API 호출
    const response = await createApiClient('v2-beta')
      .get('/wafers');
    
    // 새로운 응답 구조 처리
    return transformResponse(response.data);
  } catch (error) {
    // 실패시 이전 버전 API 사용
    const fallbackResponse = await createApiClient('v1')
      .get('/wafers');
    return fallbackResponse.data;
  }
};

2. 이슈 트래킹

질문

  • "이슈 관리는 어떤 도구를 사용하나요?"
  • "버그 리포트는 어떤 형식으로 작성해야 하나요?"

예상 답변

// 패턴 1: Jira 사용
"Jira에서 이슈 관리
- Bug: 버그 리포트
- Task: 일반 작업
- Story: 기능 개발"

// 패턴 2: GitHub Issues
"GitHub Issues로 관리
PR과 이슈 연동하여 사용"

프론트엔드 적용

// src/utils/error-reporting.ts
interface BugReport {
  title: string;
  steps: string[];
  expected: string;
  actual: string;
  environment: {
    browser: string;
    os: string;
    screenSize: string;
  };
  logs?: string;
}

const createBugReport = async (error: Error, context: any): Promise<BugReport> => {
  const report = {
    title: error.message,
    steps: context.userActions || [],
    expected: context.expectedBehavior,
    actual: error.stack,
    environment: {
      browser: navigator.userAgent,
      os: navigator.platform,
      screenSize: `${window.innerWidth}x${window.innerHeight}`
    },
    logs: await collectLogs()
  };

  // Jira API로 이슈 생성
  await createJiraIssue({
    project: 'FRONTEND',
    issueType: 'Bug',
    ...report
  });

  return report;
};

3. 코드 리뷰

질문

  • "코드 리뷰 프로세스는 어떻게 되나요?"
  • "PR 템플릿이나 커밋 메시지 규칙이 있나요?"

예상 답변

// 패턴 1: PR 기반 리뷰
"- PR 생성 시 최소 1명의 승인 필요
- PR 템플릿 준수
- CI 통과 필수"

// 패턴 2: 페어 리뷰
"- 페어 프로그래밍 권장
- 주간 코드 리뷰 미팅
- 코드 품질 메트릭 체크"

프론트엔드 적용

// .github/pull_request_template.md
/**
 * ## 변경사항
 * - 
 * 
 * ## 테스트 방법
 * 1. 
 * 2. 
 * 
 * ## 스크린샷
 * 
 * ## 체크리스트
 * - [ ] 테스트 코드 작성
 * - [ ] 문서 업데이트
 * - [ ] 브라우저 호환성 체크
 */

// src/utils/git-hooks/commit-msg
const commitRules = {
  pattern: /^(feat|fix|docs|style|refactor|test|chore)(\([a-z]+\))?: .+/,
  examples: [
    'feat: 로그인 기능 추가',
    'fix(auth): 토큰 갱신 버그 수정',
    'docs: API 문서 업데이트'
  ]
};

4. 커뮤니케이션 채널

질문

  • "일상적인 소통은 어떤 채널을 사용하나요?"
  • "긴급 상황 발생시 연락 체계는 어떻게 되나요?"

예상 답변

// 패턴 1: 도구별 용도
"- Slack: 일상 소통
- Email: 공식 문서/결정사항
- Teams: 화상 회의
- Jira: 이슈 관리"

// 패턴 2: 상황별 채널
"- 일반 문의: 팀 채널
- 긴급 장애: 비상 연락망
- 코드 리뷰: PR 댓글"

프론트엔드 적용

// src/config/contact.ts
const CONTACT_CHANNELS = {
  GENERAL: {
    tool: 'Slack',
    channel: '#team-frontend',
    responseTime: '1시간 이내'
  },
  URGENT: {
    tool: 'Mobile',
    contacts: [
      { role: 'FE Lead', number: '010-****-****' },
      { role: 'BE Lead', number: '010-****-****' }
    ],
    responseTime: '15분 이내'
  },
  CODE_REVIEW: {
    tool: 'GitHub',
    process: 'PR Comments',
    responseTime: '24시간 이내'
  }
};

// src/utils/error-handling.ts
const handleCriticalError = async (error: Error) => {
  // 에러 로깅
  await logError(error);

  // Slack 알림
  await sendSlackAlert({
    channel: CONTACT_CHANNELS.URGENT.channel,
    text: `🚨 긴급 에러 발생: ${error.message}`,
    attachments: [{
      title: '에러 상세',
      text: error.stack
    }]
  });
};

효과적인 협업을 위해서는 명확한 커뮤니케이션 채널과 프로세스가 필수적입니다. 특히 프론트엔드와 백엔드 팀 간의 원활한 소통을 위해 API 변경 관리와 이슈 트래킹 프로세스를 잘 정립해두어야 합니다.

0개의 댓글