MINIWIKI
CareerSideProjectBook&Study
  • ⚡README
  • 😃ME
    • Review
      • 2025 OKR & 회고 - 회사 없이도 먹고살 수 있는 상태가 된다
        • 2025년 19주차
        • 2025년 18주차
        • 2025년 17주차
        • 소설쓰기의 쓸모
        • 2025년 15주차
        • 2025년 14주차
        • 요즘 회사생활
        • 첫 페이지 작성!
        • 큰 코 다쳤다
        • 오랜만에 좋았던 하루
        • 악순환과 반복실패
        • 2025년 12주차
        • 2025년 11주차
        • 2025년 3월 6일
        • 2025년 3월 4일
        • 2025년 3월 1일
        • 2025년 2월 회고
        • 2025년 1-2월 책, 영화, 음악
        • 2025년 1-2월 회고 (PM)
        • 2025년 1-2월 회고 (콘제품)
          • (Merged) 2025 비즈니스
        • 2025년 1-2월 회고 (삶/사람)
        • 2025년 1-2월 회고 (기본)
        • (25.02) 고객 피드백 받기
        • 다시 전략 수정
        • 머리 속 복잡한 것들 끄적끄적
        • 변하지 않는 핵심 철학
        • 개별화 능력을 이용하는 방법
        • 파고들기
        • 예술가와 사업가
        • 강점
        • PM으로서의 전문성
        • 부동시
        • 이게 다 무슨 소용인가
        • 내가 가장 잘 전할 수 있는 메시지
        • 연말인사 타이밍
        • Attitude는 옷부터
        • 다시 시작
      • 2024 회고
        • 2024년 12월 4주차 (52/52)
        • 원한다고 생각했던 것들
        • 2024년 12월 3주차 (51/52)
        • 회사 vs. 퇴사
        • 2024년 12월 2주차 (50/52)
        • 2024년 11월 4주차 (47/52)
        • 2024년 11월 3주차 (46/52)
        • 2024년 11월 1주차 (44/52)
        • 혓바늘
        • 2024년 10월 3주차 (42/52)
        • 그냥, 요즘하고 있는 생각들
        • 2024년 10월 1주차 (40/52)
        • 2024년 9월 4주차 (39/52)
        • 2024년 9월 3주차 (38/52)
        • 2024년 9월 2주차 (37/52)
        • 2024년 9월 1주차 (36/52)
        • 2024년 8월 4주차 (35/52)
        • 잃어버린 보물창고
        • 분기별 프로젝트
        • 강점검사
        • 글쓰기
        • 이상적인 하루
        • 나와 아프리카
        • 한 때 나에게 힘이되었던 문장들
      • 2023 회고
        • 2023년 12월 5주차
        • 2023년 12월 4주차
        • 2023년 12월 3주차
        • 2023년 12월 2주차
        • 2023년 12월 3일
        • 2023년 12월 1주차
        • 2023년 11월 29일
        • 2023년 11월 28일
        • 2023년 11월 27일
        • 2023년 11월 18일
        • 2023년 11월 15일
        • 2023년 11월 12일
        • 2023년 11월 11일
        • 2023년 11월 1주차
        • 2023년 10월 3주차
        • 2023년 9월 4주차
        • 2023년 9월 3주차
        • 2023년 9월 2주차
        • 2023년 9월 1주차
        • 2023년 8월 4주차
        • 2023년 8월 2주차
        • 2023년 8월 1주차
        • 2023년 7월 4주차
        • 2023년 7월 3주차
        • 2023년 7월 2주차
        • 2023년 상반기 회고
        • 나태하고 욕심많은 인간은 어떻게 살아야 하나
        • 책 <어떻게 살아야 하는가>
        • 책 <당신은 결국 무엇이든 해내는 사람>
        • 복잡계를 살아가는 단순한 사람
        • 책 <모든것이 되는법>
        • 글로 신뢰를 얻었던 경험들
        • 기획은 나를 찾아가는 과정
        • 나는 왜 살아가는가
        • 장항준 감독으로부터 배우는 "삶을 대하는 자세"
        • 개발자가 말하는 감정에도 분석이 필요한 이유
      • 2022년 회고
        • problem map 작성하기
        • 삶에서 내가 해결하고 싶은 문제 (2)
        • 삶에서 내가 해결하고 싶은 문제 (1)
        • <삶의 문제> 지도 다시 꺼내보기
        • 지도 위의 29살
        • 매번 시간계획을 망치는 MBTI 'P형 인간'을 위한 5단계 인생관리법
        • 당신은 왜 프로그래밍을 공부하는가?
        • 아무 것도 아닌 내가 글을 쓰는 이유
        • 책 <여행의 이유>
        • 책 <붕대감기>
      • 2021년 회고
    • Career
      • [미리캔버스] AI 제품 PM
        • 선택과 집중
        • 어쩌면 내가 틀릴 수도 있다는 생각
        • 뾰족한 사람들과의 협업
        • AI기능 PPT로 온보딩
        • AI 제품에서 가장 중요한 것
      • [미리캔버스] 앱 PM
      • [미리캔버스] 소상공인 제품 PM
      • [미리캔버스] 2.0 PM
      • [홀로스탠딩] 백엔드 개발
      • [청년5.5] 안드로이드 개발
      • [가축대출사업] NGO Project PM
    • Insight
    • Interview
    • Public Writing
  • SIDE PROJECT
    • [Youtube] 메이킹필름
      • [Product] 청춘집 프로젝트
        • (v24.11) 청춘집 JTBD
          • (구) 청춘집 실행계획
          • (구) 플레이리스트 기획
            • 데이식스 전곡 타임라인
            • 챕터 구성
        • (v25.01) 청춘집 JTBD
          • 아이돌 굿즈 시장 조사 (공식)
          • 아이돌 굿즈 시장 조사 (비공식)
        • 제작 준비
          • 레퍼런스 - 오프라인 시집
          • a5 책 만들기
    • [Youtube] 마포구타자기
      • [mptw] JTBD
        • IKIGAI
      • [mptw] 채널 설정
        • 채널 이름 후보군
      • 시리즈 [읽는음악]
        • [읽는음악] 백로그
          • 노래 가사 콘텐츠 레퍼런스
        • ep1. 파노라마 - 이찬혁
          • 이찬혁 <ERROR>
        • ep2. 마지막 인사 (feat. 청하) - 이찬혁
        • ep3. 나의 바다에게 - 도영
        • ep4. Dattom - 백예린
        • ep5. REBEL HEART - IVE
        • ep6. Either way - IVE
        • ep7. 너와의 모든 지금 - 재쓰비(JAESSBEE)
        • ep8. 예뻤어 - DAY6
        • 6주차. 데이식스 시리즈
    • [IT] 공적인사적모임 플랫폼
      • 1. 우리 조직의 얼굴을 만들자
      • 2. 내 생에 첫 기획서 만들기 (feat. QA Driven Development)
    • [Meet] 공적인사적모임
    • [Youtube] 이상한나라의 개발자할무니
    • [Study] Disquiet PM 스터디 쿨피스
    • [IT] 서울 빵 맛집 잘알 테스트
    • [Meet] 얼리버드 모닝클럽
      • 홍보를 곁들인 2주일 운영후기
  • 잡학사전
    • 와인 원데이 클래스
    • 소설쓰기
      • <책> 소설쓰기의 모든 것 1 - 플롯과 구조
      • 유튜브 - 소설 쓰는 법
      • 강의들
      • 작가가 되려면 어떻게 해야해
    • AI
      • 생성형 AI
    • ComfyUI
      • Stable Diffusion
      • ComfyUI 준비, 설치, 설정
      • Module 구조에 대해 이해하기
      • ComfyUI
      • Core Node
    • 작사
      • 작사가 되는 법
    • 유튜브
      • 유튜버 스토리님의 부캐 성장기
      • 주언규 유튜브 초보편 (클래스101)
      • 주언규 유튜브 왕초보 편
    • 경제
      • 연금저축펀드
    • ChatGPT
    • 크롤링
      • Automatio
      • Octoparse
    • 노코드
      • 북마크 & 노코드 서비스 목록
  • PRODUCT&BUSINESS
    • Service Planning/Analysis
      • 브런치시리즈 <개발보단 고객개발>
      • baemin mart
        • 1. 시작
        • 2. 우아한형제들 & 배민상회
        • 3-1. [인터뷰] 포항에서 치킨집을 운영하시는 최사장님
        • 3-2. [인터뷰] 부산에서 족발 프렌차이즈를 운영하시는 이사장님
        • 4. <아프니까 사장이다> 커뮤니티 데이터 분석
        • 5. 문제정의 & 개선 가설
        • 6. 결론 - 역기획서
      • careerly
      • meetme
      • 배달의 민족 역기획 사례
      • 당근마켓 역기획 사례
      • 도그냥 님이 말하는 진짜 역기획
      • 도그냥의 역기획 스터디법
      • 책 <현업 기획자 도그냥의 서비스 기획 스쿨>
      • 기획서 작성하기
    • Business/Growth
      • Unsexy Business 뉴스레터에서 얻는 인사이트
      • 책 <원씽>
      • 책 <아프리카 스타트업>
      • 책 <유난한 도전>
      • 책 <함께자라기>
      • 책 <나는 돈 없어도 사업을 한다>
      • 책 <나는 장사의 신 은현장이다>
      • 책 <왜 사업하는가>
      • 책 <왜 일하는가>
      • 이제는 피칭도 유튜브로
      • 세컨드 브레인이 필요한 이유
      • 책 <타이탄의 도구들>
      • 책 <역행자>
        • <역행자> 역행자의 7단계 모델 복습
        • <역행자> 운명을 거스르는 역행자의 7단계 모델
      • 책 <월급쟁이로 시작한 38살 그녀는 어떻게 30억을 벌어 파이어족이 되었을까?>
      • 책 <파리에서 도시락을 파는 여자>
      • 책 <존리의 금융문맹탈출>
      • 책 <돈의 감각을 길러주는 경제 지식 첫걸음>
        • 금리
        • 환율
        • 주식
        • 채권
        • 부동산
        • 연금
        • 경제정책
        • 규제
        • 경제위기
    • Product-Market Fit
      • 브런치 북 <개발보단 고객 개발>
      • 책 <아이디어 불패의 법칙>
      • 고이장례연구소
      • 글쓰기로 PMF 검증하기
      • 연대 송도 캠퍼스의 40%가 사용한 서비스
      • 어웨이크코퍼레이션의 김민준 님
      • 드로우 마이 브랜드
      • 노코드로 PMF 찾는 방법
    • UI/UX
      • UX Writing Workshop
        • 4. 고객과의 관계형성 - 차별점 강화
        • 3. 비즈니스 임팩트를 만드는 글쓰기
        • 2. 후킹한 문장으로 고객 행동 이끌기
        • 1. 쉽고 정확한 문장으로 문제해결
        • What is UX Writing?
        • Reference
      • UX/UI 관련 유용한 사이트 모음
    • PM/PO
      • 책 <프로덕트 매니지먼트>
      • 책 <인스파이어드>
      • PM Wiki
      • 당신과 팀을 성장시킬 PM 직무가이드
      • PO 미신, 파랑새를 찾아서 - CPO 김용훈
      • 개발자가 생각하는 좋은 PM 나쁜 PM
      • 프로덕트 매니저는 뭐하는 사람인가
      • 토스 리더가 말하는 PO가 꼭 알아야할 개념 (2)
      • 토스 리더가 말하는 PO가 꼭 알아야할 개념 (1)
      • 책 <조직을 성공으로 이끄는 프로덕트 오너>
        • <프로덕트 오너> PO의 시간관리법
        • <프로덕트 오너> PO가 데이터 기반으로 일할 수 밖에 없는 이유
  • DATA
    • Database
      • 이 위키를 만드는데 참고한 자료들
      • 데이터 기반 의사결정
      • 데이터베이스의 종류
      • 트랜잭션과 무결성
      • 트랜잭션, 커밋, 롤백, 트랜잭션 전파
      • ERD, entity relationship diagram
      • 기본 3 - 관계, 키
      • 기본 2 - 필드, 레코드, 타입
      • 기본 1 - 엔티티, 릴레이션, 속성, 도메인
    • SQL
      • Sub Query
      • JOIN
      • 데이터 정렬셋과 유니코드
      • 자료형
      • DDL, DML
      • SELECT
      • SQL
    • MySQL
      • MSQL to MySQL Data Migration
      • MySQL Server 다운로드, 로그인
      • helpful commands
      • 문자열 자르기 SUBSTR(column, startIdx, length)
      • 특정 값을 ORDER BY 특정 값 우선 정렬 하기 (ORDER BY FIELD)
      • 이것이 MySQL이다
    • H2
      • ‼️h2 in-memory-db Table not found (this database is empty) 해결방법
  • Dev-General
    • Webmark
    • Open Source
      • 나의 첫 opensource contribution 경험기
    • Dev-Insight
      • Event
        • YOUTHCON 2022
        • INFCON 2022
      • 책 <누워서 읽는 알고리즘>
      • 책 <나는 LINE 개발자입니다>
      • 서비스에 대해 개발자가 가져야할 생각들
      • AI 시대에서 결국 살아남는 것
      • AI 시대에 개발자가 살아남는 방법
      • 주니어를 넘어서, 성장하는 개발자의 길 (인프런)
      • 아마추어와 프로의 차이
      • 개발자의 개발공부에 대하여
      • 서비스에 대해 개발자가 가져야할 생각들
      • 좋은 개발자와 인맥을 만든 노하우
      • 개발자 취업기/이직기 모음
        • 라인게임즈 백엔드 개발자 경선님
        • OKKY 미니세미나 <비전공 학원출신 SI개발자, 유명스타트업 들어간.ssul> 참석 후기
        • 비전공자에서 2억받는 아마존 엔지니어가 되기까지
        • IT 대기업 100% 합격하는 방법
  • 🏗️computer science
    • Algorithm & Data Structure
      • About this page
      • Test Review
        • Page 1
      • Big-O
        • 빅오표기법의 문제풀이
        • 피보나치 수열의 시간복잡도
      • Bit Operation
        • bit masking
      • Math
        • 합공식 / 누적합
        • 피보나치 수
        • 약수찾기
        • 소수찾기
          • 백준 1978 소수찾기
          • 백준4948 베르트랑 공준
          • 백준 8393 합
          • 백준 1929 소수구하기
        • 최대공약수 / 최소공배수
          • 백준 2824 최대공약수, BigInteger
          • 백준 2609 최대공약수, 최소공배수
        • 순열과 조합
          • 백준 15649 N과 M
        • 그 외 개념 정리
      • Recursion
        • N Queens problem
        • counting cells in a blob
        • recursion 응용 - 미로찾기
        • 순환 알고리즘의 설계
        • 순환적으로 사고하기
        • 백준 17478 재귀함수가 뭔가요
        • 백준 10870 피보나치수 5
      • Sort
        • java 에서의 정렬
        • radix sort
        • sorting in linear time
        • comparison sort 에서 최상의 시간복잡도
        • priority queue
        • heap sort
        • quick sort
        • merge sort
      • Array and List
        • 표준 라이브러리
      • Linked list
      • String
      • Stack
        • 백준 1874 스택수열
        • 백준 10828 스택 구현하기
      • Queue
        • 백준 10845 큐 구현하기
      • Heap
        • 백준 11298 절대값힙
        • 백준11279 최대힙
        • 백준1927 최소힙
      • Deque
      • Tree and Binary tree
        • Tries
        • Red-Black Tree
        • Binary Search Tree
      • Search
        • 완전 탐색
        • 이분탐색
      • Graph
        • 최단경로
        • MST 2 - prim 의 알고리즘
        • MST 1 - Kruskal 의 알고리즘
        • MST, minumum spanning tree
        • DAG, Directed Acyclic Graph
        • DFS, Depth First Search
        • BFS, Breadth First Search
      • Dynamic Programming
        • Knapsack problem
        • LCS, Longest Common Subsequence
        • matrix chain
        • 행렬 경로 문제
        • 백준 1003 피보나치 함수
        • 백준 9461 파도반 수열
        • 백준9251 LCS
      • Greedy
      • Implementation
      • LIS, Longest Increasing Subsequence
      • Two Pointer
      • Line Swipping
      • Fenwick tree
      • Backtracking
    • Computer Structure
      • 이 위키를 만드는데 참고한 자료들
      • 그래서 컴퓨터는 어떻게 동작하나요?
      • 컴퓨터의 구성
      • 컴퓨터의 역사
      • 컴퓨터 구성요소의 기능 및 이해
      • 중앙처리장치 - 마이크로 명령 - 입출력과 인터럽트
      • 중앙처리장치 - 기본 컴퓨터 프로그래밍
      • 중앙처리장치 - 프로그래밍 언어와 실행
      • 파이프라인과 벡터처리 - 데이터의 종속성 - 병렬처리와 파이프라인
      • 파이프라인과 벡터처리 - 파이프라인 구조 - 데이터/구조
      • 파이프라인과 백터처리 - 산술&명령어 파이프라인
      • 파이프라인과 벡터처리 - 파이프라인 CPU의 성능분석
      • 메모리 구조 - 메모리 시스템의 이해
      • 메모리 구조 - 효율적인 메모리 관리 정책
      • 메모리 구조 - 컴퓨터 성능 개선을 위한 메모리 관리
      • 입출력구조 - 시스템 BUS 구성 및 제어
      • 입출력 구조 - 입출력(I/O) 연결과 주소 지정
      • 입출력 구조 - 입출력 수행과 인터럽트
      • 병렬컴퓨터 구조와 성능분석 - 멀티 프로세서
      • 병렬 컴퓨터 구조와 성능 분석 - 시스템 성능 분석과 개선
    • This Is Coding Test 2021
      • 1. 출제 경향 & 파이썬 문법 부수기
      • 2. 그리디 알고리즘 & 구현
      • 3. BFS & DFS
      • 4. 정렬 알고리즘
      • 5. 이진탐색
      • 6. 다이나믹 프로그래밍
      • 7. 최단경로 알고리즘
      • 8. 기타 그래프 이론
      • 9. 코딩테스트에서 자주 출제되는 기타 알고리즘
      • 10. 개발형 코딩테스트
    • Operating System
      • 이 위키를 만드는데 참고한 자료들
      • 운영체제란, Introduction to Operating Systems
      • 컴퓨터 시스템의 구조, Structure of Computer System
      • 프로그램의 실행, Program Execution
      • 프로세스, Process
      • 쓰레드, Thread
      • 프로세스의 생성과 종료, Start and End of Process
      • 프로세스 시스템 콜과 프로세스간의 협력, System call and Interprocess Communication
      • CPU Scheduling
      • CPU Scheduling Algorithm
      • Process Synchronization Problem
      • Initial Attempts to Solve Process Synchronization Problem
      • semaphore 와 monitor 로 synchronization 해결하기
      • 데드락, Deadlock
      • 메모리 관리, Memory Management
      • Memory Allocation
      • Virtual Memory
      • Virtual Memory 2
      • File System
      • File Systems Implementation
      • Disk Management & Scheduling
    • Network
      • 이 위키를 만드는데 참고한 자료들
      • 대규모 트래픽으로 인한 서버 과부하 해결방법
      • 유선 LAN과 무선 LAN
      • 네트워크를 이루는 장치 (L1, L2 .. L7)
      • REST API
      • HTTP 매서드
      • HTTP 상태코드
      • 직렬화와 역직렬화
      • 로그인 구현방식 2. 토큰 기반 인증방식
      • 로그인 구현방식 1. 세션 기반 인증방식
      • 웹 브라우저의 캐시 - 공통점과 차이점
      • 웹 브라우저의 캐시 - 쿠키
      • HTTP header
      • 웹 브라우저의 캐시 - 세션 스토리지
      • 웹 브라우저의 캐시 - 로컬스토리지
      • browser rendering
      • HTTPS 와 TLS - TLS 핸드쉐이크
      • HTTPS 와 TLS - 암호화
      • HTTP History
      • www.naver.com 을 주소창에 입력하고 화면에 나타나기까지의 과정
      • IP 주소 - 공인 IP와 사설 IP
      • IP 주소 - Classless,Subnet Mask, Subnetting
      • IP 주소 - Classful IP Addressing
      • IP 주소 - IPv4, IPv6
      • IP 주소 - 이진수 이해하기
      • IP 주소, MAC 주소, ARP, RARP
      • 라우팅
      • TCP 4way handshake and TIME_WAIT
      • TCP 3way handshake
      • TCP/IP - internet layer
      • TCP/IP - Transport Layer
      • TCP/IP - Application Layer
      • TCP/IP - MTU, MSS, PMTUD
      • TCP/IP 4계층, OSI 7 layer
      • 네트워크의 분류 - LAN, MAN, WAN
      • 네트워크 토폴로지와 병목현상
      • 네트워크의 기초 3
      • 네트워크의 기초 2
      • 네트워크의 기초
    • Linux
      • reference
      • sudo apt-get install / uninstall
      • vim
      • linux basic command
    • Design Pattern
      • 이 위키를 만드는데 참고한 자료들
      • static 을 자주 사용하게 되었을 때의 단점
      • 자바스크립트의 class와 static
      • 프로그래밍 컨텍스트
      • 의존성 주입 vs. 전략패턴
      • flux pattern
      • Spring MVC 패턴 적용 사례
      • MVC, MVP, MVVM pattern
      • 프록시 패턴
      • 옵저버 패턴
      • 전략패턴
      • 의존성 주입과 의존 관계 역전 원칙
      • 이터레이션 패턴
      • 추상 팩토리 매소드 패턴
      • 팩토리 메소드 패턴
      • 싱글톤 패턴
      • 디자인 패턴, 라이브러리와 프레임워크의 차이
    • Programming Basic (Go)
      • 이 위키를 만드는데 참고한 자료들
      • 트랜지스터, Trangister
      • 논리소자, Logic Element
      • 튜링과 폰 노이만, Turing and Von Neumann
      • 컴퓨터의 원리, Computer Principle
      • 프로그래밍 언어, Programming Language
      • 컴파일러와 동적언어, Compiler and dynamic language
      • golang
      • hello, world
      • variable
      • variable 2
    • Base Knowledge
      • 이 위키를 만드는데 참고한 자료들
      • 신기술 도입시 고민해야할 점(feat. react.js vs. vue.js)
      • 정적 타입 시스템의 필요성
      • 도커, 컨테이너
      • 클라우드, Saas, IaaS, PaaS
      • SSO
      • RBAC
      • OAuth2.0
      • REST API 사용을 위한 인증 방법 4가지
      • API
      • Data Format - XML
      • Data Format - JSON
  • ☕Java/Spring
    • Java
      • Java Code Convention
      • Java 버전별 특징 (v1-v19)
      • java.lang.Math
      • List 4가지의 초기화 방법
      • HashMap 4가지의 정렬 방법
      • 어노테이션 프로세서 정리하기
      • Annotation Processor 로 없는 소스코드 생성하기
      • lombok 은 어떻게 동작하는 것일까?
      • 다이내믹 프록시 정리하기
      • 클래스의 프록시
      • 다이내믹 프록시
      • 프록시 패턴은 무엇인가
      • Spring Data JPA 는 어떻게 동작할까?
      • reflection api 정리
      • reflection api 이용하여 spring ioc container 만들기
      • reflection api
      • spring dependency injection 은 어떻게 동작할까
      • 바이트 코드 조작하기
      • java bytecode 를 조작해 테스트 코드 커버리지 확인하기 (feat.jacoco)
      • Class Loader
      • JVM 의 구조
      • java, jvm, jdk and jre
      • synchronized
      • java string.split(".") 오류
    • Java 8
      • 이 위키를 만드는데 참고한 자료들
      • Metaspace
      • Parallel 정렬
      • Annotation
      • CompletableFuture
      • Date and Time
      • Optional
      • Stream
      • interface의 default 메소드와 static 메소드
      • 인터페이스의 변화
      • 함수형 인터페이스
      • java 8 소개
    • Spring Framework
      • Spring 3.0 준비하기
      • 특정 매소드만 transaction 처리하기
      • 스프링 프로젝트 시작하기
      • 스프링이란 무엇인가
      • 스프링 핵심 기술의 응용
      • AOP 2
      • AOP 1
      • 서비스 추상화 2
      • 서비스 추상화 1
      • 예외
      • 템플릿
      • 테스트
      • 오브젝트와 의존관계
      • 스프링이란
    • Spring Boot
      • [Gradle]UncheckedIOException
      • java19 + spring 3.0.5 + gradle 7.4.1 에서 프로젝트 gradle 설정하기
      • [리뷰] Gradle 멀티 프로젝트 관리
      • [리뷰] 멀티모듈 설계 이야기 with Spring, Gradle
    • JPA/QueryDSL
      • querydsl 을 쓰는 이유
      • JPA querydsl에서 json array 로 된 컬럼에 조건 적용하기
      • querydsl 에서 mysql order by field() 사용하기
  • 🏰Infrastructure
    • InfraWorkshop
      • 이 위키를 만드는데 참고한 자료들
      • aws로 안정적인 인프라 만들기 2
      • aws로 안정적인 인프라 만들기 1
      • 어플리케이션 진단하기
      • 서버 진단하기
      • 부하 테스트
      • 웹 성능 개선하기
      • 웹 성능 진단하기
      • <aws로 그럴듯한 인프라 만들기> 회고와 피드백
      • aws로 그럴듯한 인프라 만들기 3 - 배포스크립트
      • aws로 그럴듯한 인프라 만들기 2 - 배포하기
      • aws로 그럴듯한 인프라 만들기 1 - 네트워크 망 구성
      • docker container
      • connection check
      • network segmentation
      • cloud 서비스를 사용한다는 것
    • AWS
      • AWS IAM
      • AWS CodePipeline 으로 배포 자동화하기 (1)
      • AWS CodePipeline 으로 배포 자동화하기 (2)
  • 🪄Test
    • TDD
      • 이 위키를 만드는데 참고한 자료들
      • [2주차] 로또 과제 강의를 듣고나서
      • [1주차] 자동차 경주 과제 강의를 듣고나서
      • TDD, 리팩토링이란?
      • 가장 쉽게 TDD 시작하는 방법
      • 의식적인 연습과 학습 테스트
      • TDD 에 집착해야하는 이유
      • 공부하는 자세
    • AssertJ
      • 이 위키를 만드는데 참고한 자료들
    • JUnit
      • 이 위키를 만드는데 참고한 자료들
      • Junit 기본 개념
  • 😎OTHERS
    • Helpful Command
      • Mac 에서 특정 포트 검색, 종료
      • crontab
    • Llibrary
    • IntelliJ
      • 내가 좋아하는 커스텀 세팅
    • GIT
      • Github ID/Token 한번 입력 후 저장하기
      • Github Actions
      • github organization private repository push 안될 때 (not found issue)
      • commands
      • git commit convention
    • Logging
      • logback + webfilter 로 로그설정
      • ‼️log4j 보안 이슈
    • Postman
      • postman 의 header에서 언더바(_) 변수 인식 안되는 현상
Powered by GitBook
On this page
  • 스프링이 주는 핵심 가치 2가지
  • 테스트의 필요성
  • UerDaoTest 개선하기
  • 개발자를 위한 테스팅 프레임워크, JUnit
  • JUnit 테스트로 전환하기
  • 테스트 결과의 일관성 유지하기 - 데이터베이스 선처리
  • count() 매소드 테스트 추가하기
  • 예외상황에 대한 테스트
  • 테스트 시 주의할 점‼️
  • 테스트 주도의 개발, TDD
  • 테스트 코드 개선하기
  • 스프링 테스트 적용
  • @Autowired
  • DI 와 테스트
  • 테스트에 DI 를 이용하는 방법 3가지
  • 학습 테스트로 배우는 스프링
  • 학습 테스트의 장점
  • 버그테스트

Was this helpful?

  1. Java/Spring
  2. Spring Framework

테스트

Previous템플릿Next오브젝트와 의존관계

Last updated 2 years ago

Was this helpful?

자바, 스프링 개발자들의 종착역이자, 주기적으로 다시 돌아오는 그곳, <>을 드디어 읽는다.

한창 개발 걸음마를 막 떼고 스프링으로 아장아장 기어다니며 CRUD를 할 때즈음, 토비의 스프링을 찬양하고 있는 많은 개발자들을 보았고, 역시 지식이 깊어지려면, 책을 통해서 지식을 정리하고 깊이를 다지는 시간이 필요하겠구나, 생각했다. 하지만, 개발을 이제 막 배운 그때의 나에게 토비 책은 너무나도 무시무시했고, 토비책을 읽다가 개발을 포기한 사람들의 증언도 여럿 읽고 나니, 이 책은 개발자로서 레베루업이 필요할 때즈음, 다시 꺼내봐야겠다고 생각했다.

그리고 지금이다. 퇴사 후, 이직을 준비하는 동안, 지식의 깊이를 좀 더 다져놓고 싶다는 생각이 들었고, 집앞 도서관에서 (아무도 빌려가지 않는) 토비님의 책을 빌려서 읽기 시작했다. 처음에는 가볍게 읽기 시작했지만, 읽을 수록 인사이트가 쌓여, 기록하면서 제대로 읽고 싶어졌다. 그래서 나의 세컨 브레인인 이곳 블로그에 짧은 글들로 그 내용을 적어보며, 토비님으로부터 얻은 인사이트를 내것으로 만들어보고자 한다.

스프링이 주는 핵심 가치 2가지

  1. 객체지향설계

  2. 테스트

테스트 없이는 스프링도 의미없다.

테스트의 필요성

  • 일반적으로 기능하나를 개발한다고 하면, DAO 를 테스트하기 위해서는 데이터를 표시해줄 화면, 컨트롤러, 서비스 단까지 실제 테스트하고자 하는 것보다 더 많은 양의 코드를 미리 작성해두어야 한다.

    • 이 경우, 핵심 기능 자체만 테스트하기가 어렵다. 테스트의 과정이 길기 때문에 어디서에 에러가 났는지 디버깅하기가 쉽지 않기 떄문이다.

  • 작은 단위의 테스트가 필요하다. 단위테스트라고 한다. 테스트 코드 역시 한번에 하나의 관심사만 갖고 있을 수 있도록 해야한다.

  • 단위테스트를 하면 각 기능당 발생할 수 있는 버그는 웬만큼 잡고 어플리케이션이 출시되기 때문에 실제 QA 에서 보고되는 오류의 수가 월등히 줄어들 수 있다.

  • 자동수행되는 테스트코드가 바람직하다. 매 테스트 때마다 DB 에 조작을 가하거나, 추가적인 작업이 필요하다면, 테스트를 반복해서 하기도 어렵고 쉽게 넘어가버리게 된다.

UerDaoTest 개선하기

현재 UserDaoTest 는 두 가지 문제점이 있다.

  1. 데이터의 입력 등은 자동화했지만, 여전히 테스트 결과는 화면에 출력되고, 사람의 눈으로 직접 확인해야한다.

  2. 테스트를 위해서 main() 매소드를 계속 직접 실행시켜줘야한다.

필요한 것

  1. 테스트 결과 검증의 자동화

  2. 테스트를 위한 데이터 베이스 상태 원복

개발자를 위한 테스팅 프레임워크, JUnit

JUnit 테스트로 전환하기

  • 테스트 지원 도구로써 자바에서 단위테스트 하기에 유리하다.

  • JUnit 테스트로의 전환

    • JUnit 테스트 프레임워크를 이용함으로써, IoC 가 발생하게 된다. 어플리케이션 실행을 위한 오브젝트를 생성, 실행하는 일은 모두 프레임워크에게도 제어권이 넘어온다. 따라서 main() 매소드는 더이상 필요하지 않다.

  • 테스트 메소드의 전환

    • JUnit 프레임워크가 요구하는 조건 2가지

      • 메소드는 public 으로 선언한다.

      • 테스트를 위한 메소드에 @Test 어노테이션을 붙여준다.

  • 검증코드의 전환

    • if, else 로 구성된 결과 검증 코드를 assertThat() 스태틱 메소드를 이용하여 변경한다. assertThat() 은 첫번째 파라미터를 두번째 파라미터로 오는 매처라고 불리는 조건과 비교하여 일치하면 넘어가고, 아니면 테스트가 실패하도록 해준다.

  • JUnit 테스트 실행하기

    • 스프링 컨테이너와 마찬가지로 JUnit 프레임워크도 자바코드로 만들어진 프로그램이니까, 어디서든 한번은 실행을 시켜줘야한다.

    • 아무 곳에서 JUnitCore 클래스의 main 매소드를 호출해주는 간단한 코드를 넣어준다.


public class UserDaoTest {
    public UserDaoTest() {
    }

    @Test
    public void andAndGet() throws SQLException {
        ApplicationContext context = new GenericXmlApplicationContext(new String[]{"applicationContext.xml"});
        UserDao dao = (UserDao)context.getBean("userDao", UserDao.class);
        
        User user = new User();
        user.setId("gyumee");
        user.setName("박성철");
        user.setPassword("springno1");
        
        dao.add(user);

        User user2 = dao.get(user.getId());

        Assert.assertThat(user2.getName(), CoreMatchers.is(user.getName()));
        Assert.assertThat(user2.getPassword(), CoreMatchers.is(user.getPassword()));
    }

    public static void main(String[] args) {
        JUnitCore.main(new String[]{"springbook.user.dao.UserDaoTest"});
    }
}

테스트 결과의 일관성 유지하기 - 데이터베이스 선처리

  • 현재의 UserDaoTest 라면, 이전 테스트의 결과로 남아있는 DB 에 중복된 데이터가 있을 수 있다. 이를 방지하기 위해서는 UserDaoTest 의 addAndGet() 테스트가 끝나면 자동으로 추가된 사용자 정보를 삭제해서 테스트 이전의 상태로 데이터베이스를 만들어주는 것이 필요하다.

  • 이를 위해서는 기능을 추가해야한다.

    • deleteAll() : 모든 사용자를 삭제하는 기능

    • getCount() : 현재 데이터베이스에 존재하는 모든 사용자의 수를 카운트하는 기능


public class UserDao {
    private DataSource dataSource;

    public UserDao() {
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void add(User user) throws SQLException {
        ...
    }

    public User get(String id) throws SQLException {
        ...
    }

    public void deleteAll() throws SQLException {
        Connection c = this.dataSource.getConnection();
        PreparedStatement ps = c.prepareStatement("delete from users");
        ps.executeUpdate();
        ps.close();
        c.close();
    }

    public int getCount() throws SQLException {
        Connection c = this.dataSource.getConnection();
        PreparedStatement ps = c.prepareStatement("select count(*) from users");
        ResultSet rs = ps.executeQuery();
        rs.next();
        int count = rs.getInt(1);
        rs.close();
        ps.close();
        c.close();
        return count;
    }
}


public class UserDaoTest {
    public UserDaoTest() {
    }

    @Test
    public void andAndGet() throws SQLException {
        ApplicationContext context = new GenericXmlApplicationContext(new String[]{"applicationContext.xml"});
        UserDao dao = (UserDao)context.getBean("userDao", UserDao.class);
        
        dao.deleteAll();

        Assert.assertThat(dao.getCount(), CoreMatchers.is(0));

        User user = new User();
        user.setId("gyumee");
        user.setName("박성철");
        user.setPassword("springno1");
        dao.add(user);

        Assert.assertThat(dao.getCount(), CoreMatchers.is(1));

        User user2 = dao.get(user.getId());

        Assert.assertThat(user2.getName(), CoreMatchers.is(user.getName()));
        Assert.assertThat(user2.getPassword(), CoreMatchers.is(user.getPassword()));
    }
}
  • deleteAll() 을 하는 시점은 테스트를 마치기 직전이나 테스트를 시작하기 전일 수 있다. 이때, 테스트를 시작하기 전에 호출해주는 것이 좋은데, 이유는 테스트 직전에 외부 요소에 의해 데이터베이스에 추가된 임의의 데이터들도 모두 날려주고 데이터베이스를 테스트를 하기 위해 딱 좋은 상태로 만들어주기 때문이다.

count() 매소드 테스트 추가하기

public class UserDaoTest {
    public UserDaoTest() {
    }

    @Test
    public void andAndGet() throws SQLException {
        ...
    }

    @Test
    public void count() throws SQLException {
        ApplicationContext context = new GenericXmlApplicationContext(new String[]{"applicationContext.xml"});
        UserDao dao = (UserDao)context.getBean("userDao", UserDao.class);
        User user1 = new User("gyumee", "박성철", "springno1");
        User user2 = new User("leegw700", "이길원", "springno2");
        User user3 = new User("bumjin", "박범진", "springno3");
        
        dao.deleteAll();
        Assert.assertThat(dao.getCount(), CoreMatchers.is(0));
        
        dao.add(user1);
        Assert.assertThat(dao.getCount(), CoreMatchers.is(1));
        
        dao.add(user2);
        Assert.assertThat(dao.getCount(), CoreMatchers.is(2));
        
        dao.add(user3);
        Assert.assertThat(dao.getCount(), CoreMatchers.is(3));
    }
j

예외상황에 대한 테스트

  • 만약에 id 가 존재하지 않는 회원을 조회하려고 시도했을 때는 어떻게 할까?

  • 기존의 코드 대로라면, 테스트에서 exception 을 발생시킬 것이다. 이 경우, 테스트의 예외상황을 역시 사용자가 직접 확인해야한다는 점에서 좋지 않다. 예외가 발생하는 상황에 대해서 미리 코드를 만들어 테스트해보자.

  • UserDao > get() 내부에서 id 값에 맞는 사용자가 없을 경우에 대해서 exception 을 발생시키도록 한다.

  • 테스트에서는 exception 에 대한 expectation 테스트를 넣어주면 된다.

public class UserDao {
    private DataSource dataSource;

    public UserDao() {
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void add(User user) throws SQLException {
        ... 
    }

    public User get(String id) throws SQLException {
        Connection c = this.dataSource.getConnection();
        PreparedStatement ps = c.prepareStatement("select * from users where id = ?");
        ps.setString(1, id);
        ResultSet rs = ps.executeQuery();
        User user = null;
        if (rs.next()) {
            user = new User();
            user.setId(rs.getString("id"));
            user.setName(rs.getString("name"));
            user.setPassword(rs.getString("password"));
        }

        rs.close();
        ps.close();
        c.close();
        if (user == null) {
            throw new EmptyResultDataAccessException(1);
        } else {
            return user;
        }
    }

    public void deleteAll() throws SQLException {
        ...
    }

    public int getCount() throws SQLException {
        ...
    }
}



public class UserDaoTest {
    @Test(
        expected = EmptyResultDataAccessException.class
    )
    public void getUserFailure() throws SQLException {
        this.dao.deleteAll();
        Assert.assertThat(this.dao.getCount(), CoreMatchers.is(0));
        this.dao.get("unknown_id");
    }
}

테스트 시 주의할 점‼️

  • 개발자들은 자신들이 생각한 로직으로 테스트를 짜기 때문에 성공케이스만 테스트하는 경향이 있다.

  • 스프링의 창시자 로드 존슨은 항상 "네거티브 테스트를 먼저 만들라"는 조언을 했을 정도이다.

테스트 주도의 개발, TDD

  • 테스트 코드를 먼저 만들고, 테스트를 성공하게 해주는 코드를 작성하는 방식의 개발방법

  • 실패한 테스트를 성공시키기 위한 목적이 아닌 코드는 만들지 않는다. 이 원칙을 따른다면, 만들어진 모든 코드는 빠짐없이 테스트로 검증된 것이다.

  • 테스트를 먼저 만들고 그 테스트 상황을 충족시키기 위한 기능을 구현하면, 테스트와 기능 구현 사이의 사이클이 짧아지고, 코드에 대한 피드백을 받는 주기도 짧아지게 된다. 서버를 올리거나 사전 작업을 할 필요없이 테스트를 만들고 기능을 만들고 이 과정을 반복하면 된다.

  • TDD 장점

    • 코드를 만들고 테스트 실행하는 그 사이 시간 간격이 매우 짧다.

    • 테스트는 코드를 가능한 빨리 실행할 수 있다.

  • 개발자들이 테스트코드를 안짜는 이유

    • 자바 엔터프라이즈 어플리케이션에 테스트를 만드는 것이 어렵다고 생각한다.

      • 실제로 스프링 이전에는 어려웠다.

테스트 코드 개선하기

  • JUnit 이 하나의 테스트 클래스를 가져와 테스트를 수행하는 방식

    • 테스트 클래스에서 @Test 가 붙고 + public + void 형이며 파라미터가 없는 테스트 매소드를 모두 찾는다.

    • 테스트 클래스의 오브젝트를 하나 만든다.

    • @Before 매소드 실행한다.

    • @Test 매소드를 호출하고 테스트 결과를 저장한다.

    • @After 매소드를 실행한다.

    • 위를 반복하고 모든 테스트 결과를 종합해서 돌려준다.

  • @Before, @Test, @After 매소드를 이용하여 테스트에서 중복되는 코드를 추출하여 테스트에 필요한 선처리, 후처리를 할 수 있다.

  • JUnit 은 각 테스트 매소드를 실행할 때마다 테스트 클래스의 오브젝트를 새로 만든다.

    • 각 테스트가 서로 영향을 주지 않고 독립적으로 실행되는 것을 보장하기 위함!

  • 모든 테스트에서 공용으로 사용되지 않는 것이라면 @Before 보다는 매소드로 추출하거나 그 기능들만 별도의 클래스에서 테스트하는 것이 좋다.

  • fixture

    • 테스트 수행시 필요한 정보나 오브젝트를 픽스처라고 한다.

    • 다른 테스트 메소드들에서 반복적으로 사용되는 것은 @Before 에서 세팅되도록 하면 편리하다.

public class UserDaoTest {
    private UserDao dao;
    private User user1;
    private User user2;
    private User user3;

    public UserDaoTest() {
    }

    @Before
    public void setUp() {
        ApplicationContext context = new GenericXmlApplicationContext(new String[]{"applicationContext.xml"});
        this.dao = (UserDao)context.getBean("userDao", UserDao.class);
        this.user1 = new User("gyumee", "박성철", "springno1");
        this.user2 = new User("leegw700", "이길원", "springno2");
        this.user3 = new User("bumjin", "박범진", "springno3");
    }

    @Test
    public void andAndGet() throws SQLException {
        ...
    }

    @Test(
        expected = EmptyResultDataAccessException.class
    )
    public void getUserFailure() throws SQLException {
        ... 
    }

    @Test
    public void count() throws SQLException {
        ... 
    }
}

스프링 테스트 적용

  • JUnit 프레임워크 덕분에 테스트가 간편해졌지만, @Before 내에서 어플리케이션 컨텍스트를 생성하는 방식은 테스트 개수가 늘어나는 만큼 반복되어 실행되므로 좋지 않다. 어차피 싱글톤으로 만들어진 UserDao 와 데이터베이스는 모두 상태를 갖고 있지 않으므로, 한번만 만들어지고 계속 반복되어 사용되어도 괜찮다.

  • @RunWith : JUnit 프레임워크의 테스트 실행방법을 확장할 때 사용하는 어노테이션

  • SpringJUnit4ClassRunner : JUnit 용 테스트 컨텍스트 프레임워크 확장 클래스. 테스트 진행하는 중에 사용할 어플리케이션 컨텍스트를 만들고 관리하는 작업을 진행해준다.

  • @ContextConfiguration : 자동으로 만들어줄 어플리케이션 컨텍스트의 설정파일 위치를 지정한 것.

    • 하나의 테스트 클래스는 물론 여러개의 다른 테스트 클래스에서도 설정파일이 같다면 하나의 어플리케이션 콘텍스트가 만들어진다. 즉, 설정파일이 같다면 어플리케이션 콘텍스트를 공유한다. 이때문에 테스트의 속도 및 성능이 엄청나게 향상되는 것이다.

      • 하나의 클래스 혹은 설정파일을 공유하는 클래스들 간에서 아래와 같이 출력해서 확인해보면 됨

        • System.out.println(this.context);

        • System.out.println(this);

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/applicationContext.xml"})
public class UserDaoTest {
    @Autowired
    ApplicationContext context;
    
    private UserDao dao;
    private User user1;
    private User user2;
    private User user3;

    public UserDaoTest() {
    }

    @Before
    public void setUp() {
        this.dao = (UserDao)this.context.getBean("userDao", UserDao.class);
        this.user1 = new User("gyumee", "박성철", "springno1");
        this.user2 = new User("leegw700", "이길원", "springno2");
        this.user3 = new User("bumjin", "박범진", "springno3");
    }

    @Test
    public void andAndGet() throws SQLException {
        ...
    }

    @Test(expected = EmptyResultDataAccessException.class)
    public void getUserFailure() throws SQLException {
        ...
    }

    @Test
    public void count() throws SQLException {
        ...
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/applicationContext.xml"})
public class UserDaoTest { ... }


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/applicationContext.xml"})
public class GroupDaoTest { ... }

//위의 두 테스트는 같은 application context 를 공유한다! 

@Autowired

  • 스프링에서 DI 를 사용하기 위한 어노테이션

  • 이 어노테이션이 붙은 인스턴스 변수가 있으면, 테스트 컨텍스트 프레임워크는 변수 타입과 일치하는 컨텍스트 내의 빈을 찾는다.

    • 타입이 일치하는 것이 존재하면, 인스턴스 변수에 주입

    • 별도의 생성자나 수정자와 같은 매소드 없이도 주입 가능

    • 별도의 DI 설정없이 필드타입 정보를 이용해 빈을 자동으로 가져올 수 있다. -> 타입에 의한 자동 와이어링

  • 스프링 어플리케이션 컨텍스트는 초기화할 때 자기 자신도 빈으로 등록한다. ApplicationContext 역시 빈 목록에 존재하는 것이다!

  • 컨텍스트 내에 타입이 일치하는 빈을 찾는다면 당연히 UserDao 타입도 있을 것이다. 따라서 context.getBean() 을 통해서 가져오는 것이 아니라 그냥 바로 dao 를 주입받는 방법으로 수정할 수 있다.

...
public class UserDaoTest {
    @Autowired
    UserDao dao;
}

DI 와 테스트

  • 인터페이스를 두고 DI 를 적용해야하는 이유 3가지

    • 소프트웨어 개발에서 절대 변하지 않는 것이란 없다. 추후 변경을 위해 작은 작업은 무리가 되지 않는다.

    • 클래스 구현방식은 변하지 않더라도 다른 기능이 추가될 여지는 있다. 그런 기능의 추가와 제거를 손쉽게 하는데 인터페이스를 두고 설계하는 것이 깔끔하다.

    • 테스트가 용이하기 때문이다. 테스트는 최소한의 단위로 진행할 수 있어야 하는데, DI 는 그것을 가능하게 해준다.

테스트에 DI 를 이용하는 방법 3가지

  1. 테스트 코드에 의한 DI

    1. 테스트 코드 setUp 시에 테스트용으로 오브젝트를 직접 만든다.

    2. 어플리케이션 컨텍스트의 구성이나 상태를 테스트 내에서 변경하지 않는 것이 원칙이다. 왜냐하면 테스트 클래스들 간에 모두 공유하여 사용 되기 떄문이다.

    3. @DirtiesContext 어노테이션을 통해 그 문제를 해결할 수 있지만, 이 경우, 어플리케이션 컨텍스트가 매번 만들어지게 된다.

  2. 별도의 설정파일을 이용한 DI 설정

    1. 테스트 환경을 위한 별도의 설정파일을 생성하여 Configuration 에서 설정해주는 것이다.

  3. 컨텍스트 없이 DI 테스트

    1. 사실 DI 는 컨테이너 없이도 가능한 작업이다. 아래와 같이 컨테이너를 사용하지 않고 직접 오브젝트를 생성하고 테스트용 DataSource 오브젝트를 만들어 직접 DI 를 해주는 방법도 있다.

    2. 하지만 이 경우, JUnit 에서 매번 새로운 테스트 오브젝트를 만들기 때문에 UserDao 가 계속 중복되어 생성된다.

//1. 테스트 코드에 의한 DI 
@DirtiesContext        
//이 클래스의 테스트에서만 어플리케이션 컨텍스트의 상태를 변경한다는 것을 알려준다. 이 어노테이션이 붙으면 테스트 컨텍스트는 어플리케이션 컨텍스트 공유를 허용하지 않는다. 
public class UserDaoTest {

    @Autowired
    private UserDao dao;
    ...
    
    @Before
    public void setUp() {
        DataSource dataSource = new SingleConnectionDataSource("jdbc:mysql://localhost/testdb", "spring", "book", true);
        dao.setDataSource(dataSource);
    }
}

//2. 별도의 설정파일을 이용한 DI 설정
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/test-applicationContext.xml"})
public class UserDaoTest {

    @Autowired
    private UserDao dao;
    ...
}

//3. 컨텍스트 없이 DI 테스트  
public class UserDaoTest {

    private UserDao dao;
    ...
    
    @Before
    public void setUp() {
        dao = new UserDao();
        DataSource dataSource = new SingleConnectionDataSource("jdbc:mysql://localhost/testdb", "spring", "book", true);
        dao.setDataSource(dataSource);
    }
}
<!--  -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans 
                  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
      <property name="driverClass" value="com.mysql.jdbc.Driver" />
      <property name="url" value="jdbc:mysql://localhost/springbook?characterEncoding=UTF-8" />
      <property name="username" value="spring" />
      <property name="password" value="book" />
   </bean>
   
   <bean id="userDao" class="springbook.user.dao.UserDao">
      <property name="dataSource" ref="dataSource" />
   </bean>
</beans>

학습 테스트로 배우는 스프링

  • 자신이 만든 코드 뿐만 아니라 타인이 만든 프레임워크나 다른 개발팀에서 만들어서 제공한 라이브러리 등에 대해서 테스트를 작성하는 것

학습 테스트의 장점

  • 다양한 조건에서 기능을 손쉽게 확인할 수 있음

  • 학습 테스트 코드를 개발 중에 참고 가능

  • 프레임워크나 제품을 업그레이드할 때 호환성 검증을 도와준다.

    • 프레임워크나 상용 제품이 업데이트 될 때, API 상에 미묘한 변화가 발생하여 기존의 잘되던 기능에 오류가 발생할 수 있다.

    • 변경을 적용하기 전에 미리 학습테스트로 체크해보면 좋다.

  • 테스트 작성에 대한 좋은 훈련이 된다.

  • 새로운 기술을 공부하는 과정이 즐거워진다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"junit.xml"})
public class JUnitTest {
    @Autowired
    ApplicationContext context;
    static Set<JUnitTest> testObjects = new HashSet();
    static ApplicationContext contextObject = null;

    public JUnitTest() {
    }

    @Test
    public void test1() {
        Assert.assertThat(testObjects, CoreMatchers.not(JUnitMatchers.hasItem(this)));
        testObjects.add(this);

        Assert.assertThat(contextObject == null || contextObject == this.context, CoreMatchers.is(true));
        contextObject = this.context;
    }

    @Test
    public void test2() {
        Assert.assertThat(testObjects, CoreMatchers.not(JUnitMatchers.hasItem(this)));
        testObjects.add(this);
        
        Assert.assertTrue(contextObject == null || contextObject == this.context);
        contextObject = this.context;
    }

    @Test
    public void test3() {
        Assert.assertThat(testObjects, CoreMatchers.not(JUnitMatchers.hasItem(this)));
        testObjects.add(this);
        
        Assert.assertThat(contextObject, JUnitMatchers.either(CoreMatchers.is(CoreMatchers.nullValue())).or(CoreMatchers.is(contextObject)));
        contextObject = this.context;
    }
}

버그테스트

  • 오류가 발생한 경우, 버그테스트를 만들어 두면 편하다. 실패하는 테스트를 만든 뒤, 해당 테스트가 통과되면 버그를 해결한 셈이 된다.

☕
토비의 스프링 3.1