cms+ 프로젝트 회고

은아·2024년 8월 13일

면접 대비 정리

목록 보기
1/5

1. 간편서명동의 설정 페이지 (프론트엔드)

이 페이지는 SettingSimpConsentPage.jsx 파일에 구현되어 있습니다.

주요 기능:

  • 간편동의 설정 조회 및 수정
  • 상품 선택
  • 결제 수단 선택
  • QR 코드 및 URL 생성
const SettingSimpConsentPage = () => {
  const [isShowModal, setIsShowModal] = useState(false);
  const [products, setProducts] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [simpleConsentId, setSimpleConsentId] = useState(null);
  const [vendorUsername, setVendorUsername] = useState('');
  const [showAllProducts, setShowAllProducts] = useState(false);

  const { selectedProducts, setSelectedProducts, checkedItems, setCheckedItems } =
    useSimpleConsentStore();

  // ... (이하 생략)
}

이 코드에서 주목할 점:

  1. React Hooks 사용: useState, useEffect 등을 사용하여 상태 관리.
  2. 커스텀 Hook 사용: useSimpleConsentStore를 통해 상태 관리.
  3. 조건부 렌더링: isLoading 상태에 따라 다른 UI 표시.

React Hooks의 장점

코드에서 React Hooks를 활용한 예시:

const [isShowModal, setIsShowModal] = useState(false);
const [products, setProducts] = useState([]);
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
  const fetchSimpleConsentAndProducts = async () => {
    setIsLoading(true);
    try {
      const simpleConsentData = await getSimpleConsent();
      // ... 데이터 처리 로직
    } catch (error) {
      console.error('데이터 가져오기 오류:', error);
    } finally {
      setIsLoading(false);
    }
  };

  fetchSimpleConsentAndProducts();
}, []);

이 코드에서 useStateuseEffect Hooks를 사용하고 있습니다.

장점:

  • 상태 관리(useState)와 부수 효과 처리(useEffect)를 클래스 컴포넌트 없이 함수 컴포넌트에서 할 수 있습니다.
  • useEffect를 통해 컴포넌트 마운트 시 데이터를 가져오는 로직을 깔끔하게 구현할 수 있습니다.
  • isLoading 상태를 통해 데이터 로딩 중 UI를 제어할 수 있습니다.

상태 관리 라이브러리와 React의 내장 상태 관리의 차이점

프로젝트에서는 커스텀 Hook을 사용한 상태 관리를 볼 수 있습니다:

const { selectedProducts, setSelectedProducts, checkedItems, setCheckedItems } =
  useSimpleConsentStore();

이는 React의 내장 상태 관리 기능을 확장한 것입니다. 큰 규모의 애플리케이션에서는 Redux나 MobX 같은 상태 관리 라이브러리를 사용할 수 있습니다. 이들은 더 복잡한 상태 로직, 미들웨어 지원, 개발자 도구 등을 제공합니다.

조건부 렌더링 구현 방법

코드에서 조건부 렌더링의 예:

{isLoading ? (
  <div>상품 목록을 불러오는 중...</div>
) : (
  <ProductSelectField
    // ... props
  />
)}

이는 삼항 연산자를 사용한 조건부 렌더링입니다. isLoading 상태에 따라 다른 컴포넌트를 렌더링합니다.

관련 면접 질문:

  • React Hooks의 장점은 무엇인가요?
  • 상태 관리 라이브러리(예: Redux, MobX)와 React의 내장 상태 관리의 차이점은?
  • 조건부 렌더링을 구현하는 방법에는 어떤 것들이 있나요?

2. 모바일 간편서명동의 페이지 (프론트엔드)

SimpConsentPage.jsx 파일에 구현되어 있습니다.

주요 기능:

  • 사용자 정보 입력
  • 계약 정보 입력
  • 결제 정보 입력
  • 서명 입력
const SimpConsentPage = () => {
  const start = 0;
  const end = 6;
  const { status, setStatus, reset } = useStatusStore();
  const { userData, setUserData, resetUserData, setUserAllData } = useUserDataStore();
  const [isVerified, setIsVerified] = useState(false);
  const isFirstRender = useRef(true);
  const { handleClickPrevious, handleClickNext: originalHandleClickNext } = useStatusStepper(
    'simpconsent',
    start,
    end
  );

  // ... (이하 생략)
}

주목할 점:
1. 커스텀 Hook 사용: useStatusStore, useUserDataStore, useStatusStepper
2. useRef 사용: 컴포넌트의 생명주기 동안 유지되는 값 저장
3. 다단계 폼 구현: 상태에 따라 다른 컴포넌트 렌더링

useRef와 useState의 차이점

코드에서 useRef 사용 예:

const isFirstRender = useRef(true);

이는 컴포넌트의 생명주기 동안 유지되는 값을 저장하는데 사용됩니다. useState와 달리 useRef의 값이 변경되어도 리렌더링을 트리거하지 않습니다.

React에서 다단계 폼 구현 방법

SimpConsentPage.jsx에서 다단계 폼 구현:

const componentMap = {
  0: Main,
  1: BasicInfo,
  2: ContractInfo,
  3: PaymentInfo,
  4: Signature,
  5: Loading,
  6: () => <Success content='자동결제 등록이 완료되었습니다!' />,
};

const Content = componentMap[status] || (() => 'error');

이 방식은 상태(status)에 따라 다른 컴포넌트를 렌더링하는 방식으로 다단계 폼을 구현하고 있습니다.

커스텀 Hook 만들기

프로젝트에서 커스텀 Hook 사용 예:

const { handleClickPrevious, handleClickNext: originalHandleClickNext } = useStatusStepper(
  'simpconsent',
  start,
  end
);

useStatusStepper 커스텀 Hook은 다단계 폼의 네비게이션 로직을 추상화하여 재사용성을 높이고 있습니다.

관련 면접 질문:

  • useRefuseState의 차이점은 무엇인가요?
  • React에서 다단계 폼을 구현하는 방법에는 어떤 것들이 있나요?
  • 커스텀 Hook을 만드는 이유와 장점은 무엇인가요?

3. 모바일 청구서 페이지 (프론트엔드)

InvoicePage.jsx 파일에 구현되어 있습니다. 주요 기능:

  • 청구서 정보 조회
  • 결제 방식 선택
  • 결제 진행
const InvoicePage = () => {
  const start = 0;
  const end = 2;
  const { invoiceId } = useParams();
  const { setInvoiceInfo, invoiceInfo } = useInvoiceStore();
  const { status, reset } = useStatusStore();
  const [paymentType, setPaymentType] = useState('BUYER');
  const { handleClickPrevious, handleClickNext } = useStatusStepper(paymentType, start, end);

  // ... (이하 생략)
}

주목할 점:
1. React Router 사용: useParams 훅을 통해 URL 파라미터 접근
2. 상태 관리: useInvoiceStore, useStatusStore 사용
3. 동적 컴포넌트 렌더링: componentMap을 통해 상태에 따른 컴포넌트 렌더링

React Router의 주요 기능과 사용 이유

코드에서 React Router 사용 예:

const { invoiceId } = useParams();

이는 URL 파라미터를 쉽게 추출할 수 있게 해주는 React Router의 useParams Hook입니다. React Router를 사용하면 SPA에서 페이지 간 네비게이션을 쉽게 구현할 수 있습니다.

동적 컴포넌트 렌더링의 장단점

코드에서 동적 컴포넌트 렌더링 예:

const Content = componentMap[status] || (() => 'error');

장점: 코드가 간결해지고, 상태에 따른 컴포넌트 렌더링 로직을 한 곳에서 관리할 수 있습니다.
단점: 복잡한 조건이 많아지면 가독성이 떨어질 수 있습니다.

React에서 상태 관리 방법들

프로젝트에서는 주로 커스텀 Hook을 이용한 상태 관리를 사용하고 있습니다.

관련 면접 질문:

  • React Router의 주요 기능과 사용 이유는 무엇인가요?
  • 동적 컴포넌트 렌더링의 장단점은 무엇인가요?
  • React에서 상태 관리를 위해 사용할 수 있는 방법들은 무엇이 있나요?

4. 통계 페이지 (프론트엔드)

StatisticPage.jsxStatistics.jsx 파일에 구현되어 있습니다. 주요 기능:

  • 회원 계약 통계 조회
  • 갱신 확률 예측
const Statistics = () => {
  const [statisticList, setStatisticList] = useState([]);
  const [filteredList, setFilteredList] = useState([]);
  const [selectedContract, setSelectedContract] = useState(null);
  const [new_member_renewal_probability, setRenewalProbability] = useState(null);
  const [isLoading, setIsLoading] = useState(false);

  // ... (이하 생략)
}

주목할 점:
1. 데이터 fetching: getStatisticList, getRenewalProbability API 호출
2. 상태 관리: 여러 useState 훅 사용
3. 데이터 시각화: DountChart 컴포넌트 사용

React에서 데이터 fetching 처리 방법

코드에서 데이터 fetching 예:

const fetchSimpleConsentAndProducts = async () => {
  setIsLoading(true);
  try {
    const simpleConsentData = await getSimpleConsent();
    // ... 데이터 처리 로직
  } catch (error) {
    console.error('데이터 가져오기 오류:', error);
  } finally {
    setIsLoading(false);
  }
};

이는 async/await를 사용한 데이터 fetching 방식입니다. React Query나 SWR 같은 라이브러리를 사용하면 캐싱, 재시도, 낙관적 업데이트 등의 기능을 쉽게 구현할 수 있습니다.

React에서 대량의 데이터 효율적으로 렌더링하는 방법

프로젝트에서는 대량의 데이터를 다루는 특별한 최적화가 보이지 않습니다. 필요하다면 가상화 기술(react-window 등)이나 페이지네이션을 고려할 수 있습니다.

데이터 시각화 라이브러리

코드에서 데이터 시각화 예:

<DountChart
  color='#07f'
  percent={new_member_renewal_probability}
  size='150px'
/>

이는 커스텀 DountChart 컴포넌트를 사용한 예입니다. 더 복잡한 시각화가 필요하다면 D3.js, Chart.js, Recharts 등의 라이브러리를 고려할 수 있습니다.

관련 면접 질문:

  • React에서 데이터 fetching을 처리하는 방법들에는 어떤 것들이 있나요?
  • React에서 대량의 데이터를 효율적으로 렌더링하는 방법은?
  • 데이터 시각화 라이브러리 사용 경험이 있나요? 어떤 것을 사용해봤나요?

백엔드 부분 회고

1. 간편서명동의 설정 (백엔드)

SimpConsentSettingService.java 파일에 구현되어 있습니다. 주요 기능:

  • 간편동의 설정 조회 및 수정
  • 사용 가능한 옵션 조회
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SimpConsentSettingService {

    private final SimpConsentSettingCustomRepository simpConsentSettingRepository;
    private final VendorCustomRepository vendorRepository;
    private final ProductRepository productRepository;
    private final ProductService productService;

    public SimpConsentSettingDto getSetting(Long vendorId) {
        SimpConsentSetting setting = simpConsentSettingRepository.findByVendorUsername(vendorId);
        if (setting == null) {
            throw new EntityNotFoundException("SimpConsentSetting not found for vendor: " + vendorId);
        }
        return convertToDto(setting);
    }

    // ... (이하 생략)
}

주목할 점:
1. Spring의 @Service 어노테이션 사용
2. 트랜잭션 관리: @Transactional 어노테이션 사용
3. 의존성 주입: 생성자를 통한 의존성 주입

Spring의 @Service 어노테이션의 역할은 무엇인가요?

답변: @Service 어노테이션은 Spring의 컴포넌트 스캔 메커니즘에 의해 자동으로 빈으로 등록되는 클래스임을 나타냅니다. 주로 비즈니스 로직을 포함하는 클래스에 사용됩니다.

코드 예시:

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SimpConsentSettingService {
    // ...
}

이 클래스는 @Service 어노테이션으로 표시되어 있어, Spring이 자동으로 이 클래스의 인스턴스를 생성하고 관리합니다. 이를 통해 다른 컴포넌트에서 이 서비스를 쉽게 주입받아 사용할 수 있습니다.

트랜잭션이란 무엇이며, Spring에서 어떻게 관리하나요?

답변: 트랜잭션은 데이터베이스의 상태를 변화시키는 하나의 논리적 작업 단위입니다. Spring에서는 주로 @Transactional 어노테이션을 사용하여 선언적으로 트랜잭션을 관리합니다.

코드 예시:

@Transactional(readOnly = true)
public class SimpConsentSettingService {
    @Transactional
    public MemberDetail processSimpleConsent(Long vendorId, SimpleConsentMemberDTO memberDTO,
                                             SimpleConsentPaymentDTO paymentDTO,
                                             SimpleConsentContractDTO contractDTO) {
        // ...
    }
}

여기서 클래스 레벨의 @Transactional(readOnly = true)는 기본적으로 모든 메서드가 읽기 전용 트랜잭션으로 동작함을 나타냅니다. 그러나 processSimpleConsent 메서드에는 별도의 @Transactional이 있어, 이 메서드는 읽기/쓰기 트랜잭션으로 동작합니다.

의존성 주입(DI)의 장점은 무엇인가요?

답변: 의존성 주입은 객체 간의 결합도를 낮추고, 코드의 재사용성과 테스트 용이성을 높입니다.

코드 예시:

@RequiredArgsConstructor
public class SimpConsentSettingService {
    private final SimpConsentSettingCustomRepository simpConsentSettingRepository;
    private final VendorCustomRepository vendorRepository;
    private final ProductRepository productRepository;
    private final ProductService productService;
}

여기서 @RequiredArgsConstructorfinal 필드를 사용하여 생성자 주입을 구현하고 있습니다. 이 방식은 컴파일 시점에 의존성 누락을 확인할 수 있고, 불변성을 보장합니다.

관련 면접 질문:

  • Spring의 @Service 어노테이션의 역할은 무엇인가요?
  • 트랜잭션이란 무엇이며, Spring에서 어떻게 관리하나요?
  • 의존성 주입(DI)의 장점은 무엇인가요?

2. 모바일 간편서명동의 (백엔드)

SimpleConsentService.java 파일에 구현되어 있습니다. 주요 기능:

  • 간편동의 처리
  • 계약 정보 조회
  • 서명 이미지 처리
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class SimpleConsentService {

    private final MemberRepository memberRepository;
    private final MemberCustomRepository memberCustomRepository;
    private final BillingCustomRepository billingCustomRepository;
    private final ProductCustomRepository productCustomRepository;
    private final ContractCustomRepository contractCustomRepository;
    private final PaymentService paymentService;
    private final ContractService contractService;
    private final KafkaMessagingService kafkaMessagingService;

    @Transactional
    public MemberDetail processSimpleConsent(Long vendorId, SimpleConsentMemberDTO memberDTO,
                                             SimpleConsentPaymentDTO paymentDTO,
                                             SimpleConsentContractDTO contractDTO) {
        // ... (이하 생략)
    }

    // ... (이하 생략)
}

주목할 점:
1. 로깅: @Slf4j 어노테이션 사용
2. 트랜잭션 관리: 메소드 레벨의 @Transactional 사용
3. 다양한 Repository와 Service 사용

Java에서 로깅의 중요성과 @Slf4j의 장점은 무엇인가요?

답변: 로깅은 애플리케이션의 동작을 추적하고 문제를 진단하는 데 중요합니다. @Slf4j는 Lombok 라이브러리에서 제공하는 어노테이션으로, 로거 인스턴스를 자동으로 생성해줍니다.

코드 예시:

@Slf4j
@Service
public class SimpleConsentService {
    public void someMethod() {
        log.info("Some information logged");
        log.error("An error occurred", exception);
    }
}

@Slf4j를 사용하면 별도의 로거 선언 없이 log 변수를 사용할 수 있어 코드가 간결해집니다.

Spring에서 Repository와 Service의 역할 차이는 무엇인가요?

답변: Repository는 데이터 접근 계층을 담당하고, Service는 비즈니스 로직을 담당합니다.

코드 예시:

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
    // 데이터 접근 메서드들...
}

@Service
public class SimpleConsentService {
    private final MemberRepository memberRepository;
    
    public void someBusinessLogic() {
        // 비즈니스 로직 구현
        Member member = memberRepository.findById(1L).orElseThrow();
        // 추가 로직...
    }
}

Repository는 데이터베이스와의 직접적인 상호작용을 담당하고, Service는 이를 활용하여 더 복잡한 비즈니스 로직을 구현합니다.

관련 면접 질문:

  • Java에서 로깅의 중요성과 @Slf4j의 장점은 무엇인가요?
  • Spring에서 Repository와 Service의 역할 차이는 무엇인가요?
  • JPA를 사용할 때의 장단점은 무엇인가요?

3. S3 이미지 업로드 (백엔드)

FileUploadController.java 파일에 구현되어 있습니다. 주요 기능:

  • S3에 파일 업로드
@RestController
@RequestMapping("/api/v1/simple-consent/sign")
@RequiredArgsConstructor
public class FileUploadController {

    private final AmazonS3 amazonS3Client;

    @Value("${cloud.aws.s3.bucket}")
    private String bucket;

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) {
        // ... (이하 생략)
    }
}

주목할 점:
1. AWS S3 클라이언트 사용
2. @Value 어노테이션을 통한 설정 값 주입
3. MultipartFile을 통한 파일 업로드 처리

AWS S3를 사용하는 이유와 장점은 무엇인가요?

답변: AWS S3는 확장성, 데이터 가용성, 보안 및 성능을 제공하는 객체 스토리지 서비스입니다.

코드 예시:

@RestController
public class FileUploadController {
    private final AmazonS3 amazonS3Client;

    @PostMapping("/upload")
    public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) {
        String fileName = UUID.randomUUID().toString();
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(file.getContentType());
        
        amazonS3Client.putObject(new PutObjectRequest(bucketName, fileName, file.getInputStream(), metadata));
        
        return ResponseEntity.ok("File uploaded successfully");
    }
}

이 코드는 S3에 파일을 업로드하는 과정을 보여줍니다. S3를 사용하면 대용량 파일 저장 및 관리가 용이하며, CDN과의 연동도 쉽게 할 수 있습니다.

관련 면접 질문:

  • AWS S3를 사용하는 이유와 장점은 무엇인가요?
  • Spring에서 설정 값을 주입하는 방법들에는 어떤 것들이 있나요?
  • 파일 업로드 시 보안 고려사항에는 어떤 것들이 있나요?

4. 통계 및 머신러닝 (백엔드 - Spring & Python)

Spring 부분: MemberContractStatisticController.java
Python 부분: member.ipynb

Spring:

@RestController
@RequestMapping("/api/v1/statistics")
@RequiredArgsConstructor
@Slf4j
public class MemberContractStatisticController {

    private final MemberContractStatisticService memberContractStatisticService;

    @Value("${host.analysis}")
    private String ANALYSIS_URL;

    @GetMapping("/member-contracts")
    public ResponseEntity<List<MemberContractStatisticDto>> getMemberContractStatistics(@VendorId Long vendorId) {
        // ... (이하 생략)
    }

    @PostMapping("/notebook/member.ipynb")
    public MemberContractStatisticRes getContractPred(@RequestBody MemberContractStatisticDto request) {
        // ... (이하 생략)
    }
}

Python (Jupyter Notebook):

def train_and_save_model():
    # ... (모델 학습 및 저장 로직)

def load_model_and_predict(new_data):
    # ... (모델 로드 및 예측 로직)

def execute_model(member_data):
    # ... (모델 실행 로직)

주목할 점:
1. Spring과 Python 연동: WebClient를 통한 HTTP 요청
2. 머신러닝 모델 학습 및 예측
3. 데이터 전처리 및 모델 평가

Spring에서 외부 API를 호출하는 방법들에는 어떤 것들이 있나요?

답변: RestTemplate, WebClient, Feign Client 등이 있습니다.

코드 예시 (WebClient 사용):

@Service
public class ExternalApiService {
    private final WebClient webClient;

    public ExternalApiService(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("https://api.example.com").build();
    }

    public Mono<String> callExternalApi() {
        return webClient.get()
                .uri("/some-endpoint")
                .retrieve()
                .bodyToMono(String.class);
    }
}

WebClient는 비동기-논블로킹 방식의 HTTP 요청을 지원하여 효율적인 리소스 사용이 가능합니다.

머신러닝 모델을 실제 서비스에 적용할 때 고려해야 할 점들은 무엇인가요?

답변: 모델의 성능, 실시간 처리 여부, 확장성, 모니터링, 데이터 보안 등을 고려해야 합니다.

코드 예시:

@RestController
public class PredictionController {
    private final ModelService modelService;

    @PostMapping("/predict")
    public ResponseEntity<PredictionResult> predict(@RequestBody InputData input) {
        PredictionResult result = modelService.predict(input);
        // 결과 로깅, 모니터링 메트릭 업데이트 등
        return ResponseEntity.ok(result);
    }
}

이 코드는 머신러닝 모델의 예측 결과를 반환하는 API를 보여줍니다. 실제 구현 시에는 성능 모니터링, 에러 처리, 모델 버전 관리 등의 추가 로직이 필요할 것입니다.

관련 면접 질문:

  • Spring에서 외부 API를 호출하는 방법들에는 어떤 것들이 있나요?
  • 머신러닝 모델을 실제 서비스에 적용할 때 고려해야 할 점들은 무엇인가요?
  • Python과 Java를 함께 사용하는 프로젝트에서의 장단점은 무엇인가요?
profile
Junior Developer 개발 기술 정리 블로그

0개의 댓글