OKKY Conference 2018 The Real TDD - TDD 제대로 알기 -
#1 테스트하기 쉬운 코드로 개발하기
정진욱 / PUBLYTO CPO- 테스트하기 쉬운 코드란?
- 항상 같은 결과 반환
- 외부상태를 변경하지 않음
- 테스트하기 쉬운 코드로 개발하기
- 방법1: 테스트하기 쉬운코드와 어려운 코드 분리
- 요청한 좌석 수가 확보 가능한지 판단하기 위한 코드를 테스트 하기 위해서는 DB에 테스트 데이터를 설정해야한다.
- 방법2 : 두 분류의 코드는 어디서 만나야 하나?
- 두 분류의 코드는 최대한 가장 자리에 위치(에외 : 로깅, 퍼사드)
- 테스트 하는 메소드에서 테스트 하기 어려운 코드가 제일 안쪽에 있으면 어려움이 전파되서 전체적으로 테스트하기 어려운 메소드가 된다.
- 방법3 : 두 부류 코드가 만나는 가장자리는 어떻게 테스트 해야 하나?
- 가장 자리를 테스트하는 방법을 익히자
- 수동테스트
- postman 사용
- DB의 경우 query
- 자동테스트
- 작성된 프로덕션 코드의 사용 강제
- 현 상태의 코드로는 할 수 없다
- 실제 클래스 대신 목 사용을 위해 이음새가 있어야 한다.
- 목 사용
- 작성된 코드 사용을 강제할 수 있다.
- 목 사용이 큰 장점으로 보이지만, 생각해 볼 점이 있다.
- 행위 검증
- 행위가 호출되었는가
- Mockist
- 불필요한 추상화 유발 가능성
- 상태검증
- 결과 값이 무엇인가
- Classcist
- 불필요한 추상화 필요 없음
- 행위 검증
- 작성된 코드 사용을 강제할 수 있다.
- 문제점
- 목을 남발할 가능성이 크다.
- 대부분 목 사용 예제는 간단하다. 그래서 장점이 크게 보인다.
- 실제 프로젝트에 적용하면 한꺼번에 많은 수의 목을 다루면서 곤란을 겪는다.
- 적당 수의 목 사용에 대한 답을 찾기 어렵다.
- 상태 검증으로 돌아가보자
- 목을 남발할 가능성이 크다.
- 상태 검증 - 문제 극복 방안
- TDD를 통한 사전이 아니라 사후 테스트를 하자
- 두 부류의 코드가 맞물려 잘 돌아가는 로직이다
- 난해한 코드가 아니다
- 구현된 코드를 사용하지 않고 굳이 어려운 길을 택할 이유가 없다
- 완벽을 추구하면서 목을 사용하는 비용을 들일 필요가 있는가?
- 작성된 프로덕션 코드의 사용 강제
- 가장 자리를 테스트하는 방법을 익히자
- 방법1: 테스트하기 쉬운코드와 어려운 코드 분리
- 끝으로
- 요즘 저는
- 두 분류 코드를 분리해서 각각 테스트하고,
- 가장자리에서 맞물려 돌아가는 코드는 주로 수동테스트
- 두 부류 코드 섞어 놓고 테스트가 어렵다고 포기하지 마세요
- 위 내용과 관련된 제 블로그 참조
- 요즘 저는
- 테스트를 만족하는 만큼만 코드를 작성하라
- TDD는 점진적인 접근방법을 사용하고 있다.
- 테스트하기 어렵다는 거는 테스트 할때 값이 필요하다는 뜻이다.
- Q & A
- private 코드 테스트 하는 방법?
- 같은 기능을 테스트해도 적은 메소드를 건드리는 메소드가 좋다
- 레거시 코드 테스트 하는 방법?
- 최대한 레거시 코드와 결합도를 낮추자
- 목업을 할수 있는 seam을 만든다
- 최대한 새로 생성된 코드에 최대한 테스트를 가한다
- 기존 레거시 코드에 대한 값을 정의해 놓고 테스트
- private 코드 테스트 하는 방법?
#2 의식적인 연습으로 TDD, 리팩토링 연습하기
박재성 / SW 교육 전문가 전 NEXT 교수TDD와 리팩토링을 왜 해야 하는지 알고 있다. 는 가정 하에 진행한다.
TDD와 리팩토링을 비슷한 비중으로 다룬다. 어쩌면 TDD < 리팩토링 일지도 모른다.
개발 현장을 떠난지 오래 되어서 틀린 부분이 있을 수도 있다
- 의식적인 연습으로 TDD, 리팩토링 연습 과정
- TDD, 리팩토링을 잘 하려면??
- 연습..연습..
- 연습을 많이 한다고 잘할 수 있을까?
- 6년한 후에 알게된 점
- 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
- 테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감
- 연습..연습..
- 의식적인 연습의 7가지 원칙
- 첫째, 효과적인 훈련 기법이 수립되어 있는 기술 연마
- 둘째, 개인의 컴포트 존을 벗어난 지점에서 진행, 자신의 형재 능력을 살짝 넘어가는 작업을 지속적으로 시도
- 셋째, 명확하고 구체적인 목표를 가지고 진행
- 넷째, 신중하고 계획적이다. 즉, 개인이 온전히 집중하고 '의식적'으로 행동할 것을 요구
- 다섯째, 피드백과 피드백에 따른 행동 변경을 수반
- 여섯째, 효과적인 심적 표상을 만들어내는 한편으로 심적 표상에 의존
- 일곱째, 기존에 습득한 기술의 특정 부분을 집중적으로 개선함으로써 발전시키고, 수정하는 과정을 수반
- 연습하는 과정
- 1단계 - 단위 테스트 연습
- 내가 사용하는 API 사용법을 익히기 위한 학습 테스트에서 시작
- JAVA API 테스트(split, subString, ArrayLisy ...)
- 내가 구현하는 메소드중 Input과 Output이 명확한 클래스 메소드(보통Util 메소드)에 대한 단위 테스트 연습
- 알고리즘을 학습한다면 알고리즘 구현에 대한 검증을 단위 테스트로 한다.
- 지켜야 할 원칙
- 회사 프로젝트에 연습하지 말고 장난감 프로젝트를 활용해 연습하자.
- 실패하는 테스트 소스 -> 성공하는 테스트 소스 - > 리팩토링을 반복
- 테스트 코드를 먼저 만들고 코드를 만든다.
- 어려운 문제를 해결하는 것이 아니라 TDD 연습이 목적 난이도가 낮거나 자신에게 익숙한 문제로 시작하는 것을 추천
- 내가 사용하는 API 사용법을 익히기 위한 학습 테스트에서 시작
- 2단계 - TDD연습
- 회사 프로젝트 말고 장난감 프로젝트를 활용하자.
- 의존관계를 가지지 않는 장난감 프로젝트를 활용하자.
- TDD 사이클
- 처음에는 1단계, 2단계만 하고 3단게 Refactoring은 안해도 된다.
- 리팩토링 연습
- 한 메서드에 오직 한 단계의 들여쓰기만 한다.(메서드로 분리)
- else 예약어를 쓰지 않는다.
- return을 중간에 하면 하단 코드를 안봐도 된다.
- 가독성 증가
- 메소드가 한 가지 일만 하도록 구현하기
- 로컬 변수가 정말 필요한가?
- compose method 패턴 적용
- 모든 원시값과 문자열을 포장한다.
- 일급 콜렉션을 쓴다.
- 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.(힘들다)
- 한 메서드에 오직 한 단계의 들여쓰기만 한다.(메서드로 분리)
- 테스트 코드는 변경하지 말고 테스트 대상 코드를 개선하는 연습을 한다.
- 읽기 좋게 코드를 변경한다.
- 추상적인 피드백을 받으면 어느 부분을 작업 해야할지 알기 어렵다.
- 회사 프로젝트 말고 장난감 프로젝트를 활용하자.
- 3단계 - 연습을 과하게 한다
- 연습을 할때는 극단적으로 해야 느끼는 점이 있다
- 처음 읽는 사람에게 어느 코드가 더 읽기 좋을까? 리팩토링 전, 후
- 한번에 모든 원칙을 지키면서 리팩토링하려고 연습하지 마라. 한번에 한 가지 명확하고 구체적인 목표를 가지고 연습하라.
- 연습을 극단적인 방법으로 연습하는 것도 좋다. 예를 들어 한 메소드의 라인 수 제한을 15라인 -> 10라인으로 줄여가면서 연습하는 것도 좋은 방법이다.
- 테스트 코드가 있으니 리팩토링 연습이 가능하다.
- 4단계 - 장난감 프로젝트 난이도 높이기
- 점진적으로 요구사항이 복잡한 프로그램을 구현한다. 앞에서 지켰던 기준을 지키면서 프로그래밍 연습을 한다.
- TDD하기 좋은
- 게임과 같이 요구사항이 명확한 프로그램으로 연습
- 의존관계가 없이 연습
- 약간은 복잡한 로직이 있는 프로그램
- 연습하기 좋은 예
- 로또
- 사다리 타기
- 볼링 게임 점수판
- 체스 게임
- 지뢰 찾기 게임
- 점진적으로 요구사항이 복잡한 프로그램을 구현한다. 앞에서 지켰던 기준을 지키면서 프로그래밍 연습을 한다.
- 5단계 - 의존관계 추가를 통한 난이도 높이기
- 웹, 모바일 UI, 데이터베이스의 의존관계를 테스트
- 필요한 역량
- 테스트하기 쉬운 코드와 테스트하기 어려운 코드를 보는 눈
- 테스트하기 어려운 코드를 테스트하기 쉬운 코드로 설계하는 감
- 앞 단계 연습을 잘 소솨했다면 테스트하기 쉬운 코드와 어려운 코드를 분리하는 역량이 쌓였을 것이다.
- 한 단계 더 나아간 연습을 하고 싶다면
- 컴파일 에러를 최소화하면서 리팩토링하기
- ATDD 기반으로 응용 애플리케이션 개발하기
- 레거시 애플리케이션에 테스트 코드 추가해 리팩토링하기
- 유지보수를 잘하는 SW개발자가 몸값이 더 올라갈 것이다
- 책
- 소트웍스 앤솔러지
- 클린코드
- 웹, 모바일 UI, 데이터베이스의 의존관계를 테스트
- TDD, 리팩토링 연습을 위해 필요한 것은?
- 조급한 대신 마음의 여유
- 나만의 장난감 프로젝트
- 같은 과제를 반복적으로 구현할 수 있는 인내력
- 가장 필요한 것은 가보지 않는 길에 꾸준히 도전할 수 있는 용기
- 1단계 - 단위 테스트 연습
- Q & A
- 컴포트존을 극복하는 방법?
- 삶에 여유와 에너지가 있어야 한다.
- 회사 말고 집에서 혼자 연습을 하자.
- 기존 테스트 코드가 있는데 요구사항이 추가, 변경 됐을때 기존 테스트 코드가 에러 났을때 대처
- 내가 테스트 코드를 잘못 짠 경우가 많다.
- 나중에 하지 말고 현재에 최대한 테스트 코드를 작성하자.
- 컴포트존을 극복하는 방법?
#3 코드 품질을 위한 테스트 주도 개발
삼성SDS 한성곤 Principal Engineer- TDD Cycle
- Red -> Green -> Refactor 반복
- 짧은 개발 주기의 반복에 의존하는 개발 프로세스
- XP의 Test-First 개념을 기반으로, 단순한 설계를 중요시 하는 Pratice
- TDD 장점
- FROM Test
- 동작하는 코드에 대한 자신감
- 회귀테스트를 통한 자유로은 리팩토링
- 코드에 대한 지식이 증가
- 개발 생산성 향상
- FROM Test-first
- 과도한 설계를 피하고, 간결한 interface를 가짐
- 불필요한 기능을 줄임
- 실행 가능한 문서를 가짐
- 코드 품질을 높임
- 테스트 코드가 있으면 디버깅에 대한 시간이 줄어든다.
- FROM Test
- SonarCloud
- 프로젝트의 코드 퀄리티를 분석
- 사례
- Microsoft 사례
- TDD / Non TDD 팀
- TDD가 하는 팀이 15% 더 많은 시간 소요
- TDD / Non TDD 팀
- Microsoft 사례
- 불필요한 주석은 나쁘다. 버그성에 대한 내용을 주석에 담는 경우
- 코드 퀄리티 매트릭스
- 복잡도
- 결합도
- 응집도
- MCC 측정 평가
- 마이크로소프트는 McCabe 수치를 임계치 15, 10이상은 refactoring 교려 대상
#4 테알못 신입은 어떻게 테스트를 시작했을까?
이혜승 / 오마이호텔 Front-End Engineer- 방법에 대한 이야기
- 테스트
- 1-1 기존 코드에 테스트 추가하기
- 이미 테스트 하기 쉽게 만들어져 있는 코드로 시작한다(순수함수, 외부 의존성 있는것은 말고)
- 프로덕션 코드는 수정하지 않고 테스트 한다.
- 테스트코드는 수정하지 않고 리팩토링을 진행한다.(리팩토링은 테스트가 깨지지 않는 범위 내에서만)
- 테스트 하기 어렵게 만들어져 있는 코드에서 테스트 하기 쉬운 것만 분리한다.
- 선택 기준
- 중요도가 높은 비즈니스 로직이 포함된 부분
- 버그가 발견된 부분 (과거X)
- 결합이 낮고 논리는 복잡한 부분(외부 의존도가 낮거나 없고(독립적인 환경을 가지고 있고) 비즈니스 로직은 복잡한)
- 선택 기준
- 테스트 가능한 코드를 찾아서 -> 분리하고 -> 테스트 코드를 추가하고 -> 리팩토링
- 코드가 테스트 하기 어려운 이유(API 요청,응답 결과에 따라 다른 UI)는 테스트 가능한 부분만 분리한다.
- 외부에 영향을 받지 않는 독립적인 함수로 만들어 준다.
- 반환 값에 대한 스펙을 추가한다.
- 이미 테스트 하기 쉽게 만들어져 있는 코드로 시작한다(순수함수, 외부 의존성 있는것은 말고)
- 1-1 기존 코드에 테스트 추가하기
- TDD
- 1-2 새로운 코드는 TDD로 개발하기
- 비즈니스 요구 사항 추가
- 1-2 새로운 코드는 TDD로 개발하기
- 정리
- 어떻게
- 기존 코드에 테스트만 추가(+리팩토링)
- 새롭게 추가 되는 요구 사항은 TDD
- 무엇을
- 이미 테스트 하기 쉬운 코드를 만들어서
- 어떻게
- 테스트
- 경험에 대한 이야기
- 좋은점
- 새로운 비즈니스가 추가 됐을때 코드를 수정 후 문제 점이 있을때 기존에 있는 오류를 테스트코드가 잡아준다.
- 진짜로 느낀 것들
- 스펙 문서 기능
- 디자인 개선 효과
- 학습 동기부여
- 내가 얼마나 디자인을 못하는 지 알게 된다.
- 안정성과 설계 관심을 가지게 된다.
- 개발 생산성 향상
- 버그를 추적하는 시간이 현저하게 줄어든다.
- 프로젝트 생산성 향상
- 비즈니스 로직의 허점을 사전(본격적으로 코딩을 시작하기 전에)에 발견하기도 한다.
- 집중력 향상
- TDD는 동시에 한 가지 이상의 일을 하지 않도록 통제해준다.
- 스펙 문서 기능
- 새로운 비즈니스가 추가 됐을때 코드를 수정 후 문제 점이 있을때 기존에 있는 오류를 테스트코드가 잡아준다.
- 실수
- 테스트 대상 오류
- 불필요한 테스트
- 불필요하다의 기준 : 비즈니스와 관련된 버그를 낼 가능성이 낮거나 없고 테스트를 유지함으로써 얻는 이익 < 테스트 유지와 관리에 드는 비용 일 때, 테스트가 단언하고 있는 내용이 사용자에게 중요한 가치를 주는 것이 아닐때
- 필요하지만 검증 방식이 잘못된 테스트
- 특정 위치에 대한 테스트는 변경될 소지가 많다.
- 검증식을 개선한다.
- 검증력이 떨어지는 테스트
- 하나 이상의 가능성을 열어 두고 있다.
- 테스트 제목과 검증의 불일치
- 테스트를 앞서가는 프로덕션 코드
- 프로덕션 코드에서 미리 모든 요소를 만든다.
- 테스트가 성공할 만큼의 최소한의 프로덕션 코드를 만든다의 원칙을 어긴다.
- 불필요한 테스트
- 테스트 대상 오류
- 고민
- 테스트 관련 코드들의 관리
- 테스트를 위해 만들어진 데이터(Fixture)
- 이런방식으로 하다보니 함수가 곳곳에 퍼지게 된다
- 왠지 모르겠지만 불안하다.
- 계속 이렇게 함수로 분리하기만 하는 게 맞는 건가?
- 오히려 전부 분리하는 게 가독성을 더 해치고
- 추상화 수준이 낮다
- 아주 구체적인 작은 단위의 함수들로만 분리되어 있고 그들이 조합되어 만들어지는 큰 수준의 기능은 설계되지 않았다. 그래서 추상화 수준이 낮아졌고 클라이언트에서 구체적인 내용을 알아야만 사용될 수 있다.
- 높은 응집, 낮은 결합
- 왠지 모르겠지만 불안하다.
- 테스트 관련 코드들의 관리
- 좋은점
#5 테스트를 돌보기 위한 매우 간단한 실천 방법들, 그리고 효과
양완수 / 쿠팡 Principal, Software Engineer테스트 코드의 가독성
- 질문
- TDD를 실무에서 많이 하느냐? 아니다.
- Unit Test를 작성을 하고 있느냐? 할 때도 있고 안 할 떄도 있다.
- 발표 내용
- TDD에 대해서 이야기하지 않습니다.
- 테스트하기 좋은 설계에 대해서 이야기하지 않습니다.
- 제어할 수 없는 것에 대해서 이야기하지 않습니다.
- 테스트 코드는 프로젝트 코드가 사용되는 최초의 장소이며 고객이다.
- 모든 역사는 테스트 코드부터 시작된다.
- 추상이란?
- 문맥 위에서 오직 관심 있는 것들에 대해서만 집중하여 명확하게 하는 것
- 당신이 테스트를 거들떠 보려 하지 않는 이유
- 귀찮다.
- 고통스럽게 하는것
- 숨겨진 본질
- 낮은 추상화
- 들쭉날쭉한 추상화
- 끊어진 논리
- 알 수 없는 의도
- 욕심쟁이
- 테스트가 실패하는 이유는 단 하나
- 하나의 테스트는 오직 한 가지만 똑바로 검사해야 한다.
- 인지능력의 과부화
- 흩어진 코드와 데이터
- 매직 넘버
- 꺠지기 쉬운 것들
- 높은 결합
- 낮은 응집
- 숨겨진 본질
- 귀찮다.
- 백문이 불여 일코
- 좋은 설계를 가진 코드는 아니다.
- 다양한 가느성을 가진 코드
- 실제 팀 내 코드에서 발췌하여 발표에 맞게 수정
- 불필요한 것에 집착하지 않습니다.(네이밍)
- 테스트 코드를 위한 프로덕트 코드
- url작성하기 Git
- 코드 커밋 순서 설명
- 무슨 테스트인지 알 수 없는 코드
- setUp메소드를 통해 응집력이 다른 코드를 분리하고 추상화 레벨을 맞추려고 한다.
- 객체를 새로 만들어 2가지 객체의 관계를 가깝게 만든다.
- 테스트 대상을 명확하게 표현한다.(네임변경)
- 한가지만 확실하게 테스트를 대칭성 있게 하자
- 응답에는 성공과, 실패가 있다. 의존을 제거하고 불필요한 것을 격리 한다.
- 테스트명을 처음부터 스트레스 받지 말고 안의 내용을 명확하게 만든 후 수정하자.
- 하나의 것만 테스트 해라.
- 좋은 설계를 가진 코드는 아니다.
- 테스트 코드를 잘 작성하려고 노력하다보면 좋은 설계를 가진 코드(높은 응집도 낮은 결합도를 가진 코드)를 가지게 된다.
- 테스트가 쉽도록 코드를 만들어야 한다.
- 테스트는 과정, 수련, 스승, 거울, 제품이다. 억지로 먼저 하지마라. 건강하게 하라. 두려워 하지마라. 즐거워 해라.
- 테스트는 우리를 가르쳐준다. 우리를 가이드하고 발전 시켜준다. 프로덕션 코드를 좋게 만들어 준다.
#6 당신들의 TDD가 실패하는 이유(Live Coding)
이규원 / 오마이호텔 CTO- TDD가 실패 하는 이유
- 준비가 되지 않아서
- 개인
- 모든 것에 TDD를 하지 않는다.
- 우리가 보호해야 하는 것
- 도메인
- 우리가 제어할 수 없는 것
- 외부 세상
- 실 세계
- 인프라
- 외부 서비스
- 레거시
- 외부 세상
- 설계
- 낮은 결합
- 높은 응집
- 도메인 모델 보호
- 설계를 테스트 하라
- 문제점
- 정보 숨김(Information Hiding)
- 어려운 설계 결정과 변경될 가능성이 높은 설계 결정들을 다른 모듈로부터 숨기는 것
- 구현을 테스트
- 내부의 코드를 수정하면 테스트가 깨진다.
- 설계 테스트
- 내부의 코드를 수정해도 테스트가 깨지지 않는다.
- 정보 숨김(Information Hiding)
- 레거시와 함께 살기
- 새로 작성한 코드를 위해 Adapter로 감싸고 테스트 한다.(System Under Test)
- 모든 것에 TDD를 하지 않는다.
- 팀
- 프로세스
- 점진
- 반복
- Fail-Fast
- 반복 주기
- 주로 실행만 한다.(코드만 작성)
- 계획
- 실행
- 평가
- 문화
- 공유
- 목표
- 지식
- 공유
- 아키텍처
- 낮은 결합
- 높은 응집
- 도메인 모델 보호
- 도메인 모델과 플랫폼
- 도메인 모델과 플랫폼이 강하게 연결되어 있지 말고 서로 분리 해야 한다.
- 아키텍처 사례(크기가 큰 순서로 나열)
- 비즈니스로직 - 서비스 -모듈 - 애플리케이션 플랫폼
- MVVM 아키텍처 패턴
- 프로세스
- 준비가 되지 않아서
- 비즈니스에 대한 목적을 정확하고 정리하고 그림을 그리고 얘기를 해야 서로 이해에 대한 오해가 없다
- ATDD(Acceptance Test) + TDD(Unit Test)로도 테스트 할 수 있다.
- 코드 설계
- 작업에는 어떤 코드 변경이 필요한가?
- 피드백
#7 패널 토의 Q & A
- 왜 많은 방법론 중에서 TDD인가?
- 코드에 대한 신중함이 생긴다.
- 가장 빠른 피드백을 준다.
- 피드백을 통해 직무 탈진을 하지 않게 된다.
- 주위 사람들에게 TDD를 어떻게 설득할 수 있을까?
- TDD를 하는 개발자는 많지 않다. 외롭다.
- 어떻게 같이 TDD를?
- 주니어 개발자고 영향력이 크지 않다면 전파하는데 집착하지 말자
- 나의 만족을 위해서 하자
- 코드에 대한 자신감이 생긴다.
- 주위 사람이 리팩토링한 코드에 대해 물어보면 그떄 전파 하자
- 나중에 영향력이 있을떄 전파 하자
- 하지만 과연 성공할까?
- 팀에 대한 문제점을 개인이 인식하게 만든다.
- 팀원들이 페어링과 TDD를 하자는 환경을 만들어야 한다.
- 팀원들이 주도적으로 하게 만들어야 한다.
- 먼저 TDD를 하는게 아니라 필요한 부분에 먼저 작은 변화를 적용한다.
- 초보일때 남들과 같이 TDD를 하기에 수월할 수도 있다.
- 주니어 개발자고 영향력이 크지 않다면 전파하는데 집착하지 말자
- TDD에서는 테스트의 순서가 중요 하다. 첫 테스트는 작지만 핵심인 부분을 하자
- TDD를 도입할 때도 핵심을 찾아야 한다.
- TDD를 하는 개발자는 많지 않다. 외롭다.
- 코드 커버리지의 기준은 어떠한 지표로써 이해를 해야 좋을까?
- 테스트 커버리지에 집착 하지 말자
- 프로덕션 코드에 집중 하자
- 신입의 TDD공부방법 및 도서
- 페어프로그래밍
- 테스트 주도 개발(도서)
- 테스트 및 리팩토링 책을 많이 보자
- 책만 보지 말고 꾸준히 수련 하
- 도메인 모델과 스프링의 접점을 줄여야 하는 이유
- 여러 플랫폼에 종속적이지 않기 위해서
- 스프링으로 개발하지 말고 순수 코드로 개발 완료 후 스프링에 옮겨 본다.
0 개의 댓글:
댓글 쓰기