├── .gitignore ├── README.md ├── deploy.sh ├── images ├── fastcampus-java-1.jpg ├── mappedsuperclass.png ├── messageSource.png └── resttemplate.png ├── out └── production │ └── fastcampus-java │ ├── main │ ├── Calculator.class │ └── StringCalculator.class │ └── test │ └── main │ ├── CalculatorTest.class │ └── StringCalculatorTest.class ├── review └── README.md └── src ├── main └── week1 │ ├── Calculator.java │ └── StringCalculator.java └── test └── main └── week1 ├── CalculatorTest.java └── StringCalculatorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | *.iml 3 | .idea 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 패스트캠퍼스 자바 웹 프로그래밍 CAMP 2 | 3 | * 자바지기(박재성)님의 [패스트캠퍼스 자바 웹 프로그래밍 CAMP](http://www.fastcampus.co.kr/dev_camp_jwp/) 강의 내용 정리 4 | * 광고 아니라 진심으로 150만원 상당의 수강료가 아깝지 않은 명품 강의였다. 5 | 6 | ## 1일차 - TDD 시작 7 | * 일반적인 main()을 이용한 테스트 방식의 문제점 8 | * 이클립스에서 Junit 사용법 (난 중간중간 인텔리제이로 검색해서 적용함) 9 | * @Before 사용하는 이유 : 각 테스트들간의 고유한 객체를 생성해주기 위해 10 | * 선 테스트코드 작성후 실제 코드 개발 11 | * 구현 -> 테스트 -> 리펙토링 과정으로 진행 12 | * 테스트 메소드는 production 코드에서 exception을 던질수 있기 때문에 웬만하면 throws Exception을 추가하는게 좋다 13 | * private 메소드의 테스트는 private를 지우고 테스트 or 해당 메소드를 class로 뽑아서(Refactor 사용) 진행도 가능 14 | * Refactor 사용을 활성화 하자! (Method, Class 등) 15 | 16 | ## 2일차 - HTTP 웹서버 실습 17 | * 순수 Java로 웹 서버 구축 - [프로젝트 코드] (https://github.com/jojoldu/web-application-server) 18 | * 문자열 덧셈 게시판 못다한 이야기 19 | * sum(toInts(split(text))) 을 람다식으로 표현 20 | ``` 21 | Arrays.stream(split(text)) 22 | .map(t -> Integer.parseInt(t)) 23 | .reduce(0, (number, sum) -> number + sum); 24 | ``` 25 | * RequestHandler는 Thread를 상속 받는다 26 | * connection은 대기하다가, 사용자가 접속하면 Thread 객체를 생성한다. (즉, 사용자 1명당 Thread 객체 1개가 할당된다) 27 | * 그럼 동접이 1만명 10만명인 경우 Thread가 1만개, 10만개가 생성되는가? 28 | 29 | ## 3일차 - HTTP 웹서버 리팩토링 실습 30 | * 좋은개발자란? 31 | - 20대~ 30대초반 : 기술적으로 뛰어난 개발자 32 | - 30대 중,후반 : 협업과 소통이 잘되어 같이 일하고 싶은 개발자 33 | - 현재 : 사람을 중심에 두면서 기술을 사용할줄 아는 개발자 34 | * Exception 35 | - exception은 컴파일 exception 36 | - 잘모르겠으면 throw new RuntimeException을 보낸다. 37 | - 통장에서 돈을 인출하려는데 잔액 부족할 경우 - checked exception 38 | - DB연결이 끊길 경우 - unchecked exception 39 | - 확실히 시스템 문제라면 unchecked exception, 그외에는 checked exception 40 | - appender를 이용하면 별도의 코드 추가없이 모니터링 서비스로 에러로그를 보낼수가 있다. 41 | * 복잡해진 웹 서버의 코드를 리팩토링 하자 - [프로젝트 코드] (https://github.com/jojoldu/web-application-server/tree/was-step1-bad-version) 42 | * 소켓이 생성되려면 서버의 IP/Port 와 클라이언트의 IP/Port가 필요하다 43 | * TCP three-way handshake 44 | - 3번에 걸쳐 서버와 클라이언트간 교환작업 45 | - DB와 웹서버의 경우 이 작업이 너무 크므로 웹서버와 DB간의 three-way handshake를 미리 완료하여 이를 connection pool에 담아서 이 작업을 최소화 한다. 46 | - 이게 가능한 이유는 클라이언트(즉, 여기선 DB)의 IP와 포트를 고정시킬수 있기 때문이다. 브라우저는 IP가 고정되어있지 않으므로 불가능하다. 47 | * HTTP Status 48 | - 302를 만나면 브라우저는 location 헤더 값이 있겠구나고 생각하고, location에 있는 값으로 다시 서버에 요청한다. 모든 리다이렉트 API는 이러한 방식이다. 49 | - 200은 index.html을 body에 담아서 보내는거라 리다이렉트 되지 않는다. 50 | * HTTP 상태 vs 무상태 51 | - 상태 protocol: 서버가 클라이언트의 상태값을 계속 갖고 있는 것(connection을 계속 이어간다.) 52 | - 무상태 protocol: 서버가 클라이언트에 결과를 보내면 connection을 끊어버린다. 53 | - 세션은 정보를 서버에 저장하고, 세션 ID를 쿠키로 전달한다. 54 | * CSRF 토큰: 주요 요청 직전에 발급하는 토큰으로, 이와 맞지 않으면 요청을 무효화시킨다. 55 | 56 | ## 4일차 - HTTP 웹서버 코드리뷰 57 | * Github를 통해 관리해야하는 코드는? 58 | - build를 통해 생성되는 파일 (.settings, target 등) 59 | - 의존성 관리 툴(maven, gradle, npm, bower)을 통해 다운받는 라이브러리 60 | - IDE를 통해 생성되는 파일 (.project, .iml, .springbeans 등) 61 | - 모르겠으면 [gitignore.io](https://www.gitignore.io/)에서 java로 검색해서 제외해야할것 찾아서 .gitignore에 추가하자 62 | - Gradle 과 Maven 둘다 하기보다는 1개라도 잘하자 63 | 64 | * 객체지향의 다형성에 익숙해지려면 어떻게 해야하나? 65 | - DB연동이 없고, UI를 고려하지 않는 프로젝트를 진행할것 (웹은 좋은 예제는 아니다) 66 | - ex) HTTP웹서버, 프레임워크 or 라이브러리, 체스게임, 지뢰찾기, 볼링게임점수판 등 67 | - [객체지향 생활 체조 총정리](https://developerfarm.wordpress.com/2012/02/03/object_calisthenics_summary/)의 내용을 참고 68 | - 하나의 프로젝트(주제)를 주기적으로 다시 만들어보자 여러 프로젝트를 하지 말자. 69 | - 예를 들어 전사케릭을 50까지 키웠는데 재미없어서, 마법사케릭을 다시 키워서 50까지 키우니 재미없어서 궁수케릭을 다시 키우는 식이 될수있다. 70 | - 하나의 케릭을 쭈욱 키우는게 더 높은 곳까지 갈 수 있다. 71 | 72 | * HTTP 웹서버 코드리뷰 진행 73 | - 기존에 짠 코드를 테스트하는게 아니다 74 | - 테스트를 위해 기존 코드를 고치는 것이다. 즉, 테스트를 원하는 코드를 계속 method로 빼고, 세분화하자 75 | - 테스트하기 쉬운 코드 혹은 method는 input이 있고 return이 있어야 한다. (스칼라가 지향하는 방향과 동일하네?) 76 | - private method의 경우 method보다는 class가 오히려 더 잘어울릴수 있다. (힌트가 될수 있다) 77 | - logger같은 경우 IDE에 템플릿 등록해서 사용하면 반복작업을 최소화할수 있다. 78 | - 상태값을 가지는 객체의 경우 쓰레드마다 매번 객체 생성 해야하지만, 상태값이 없는 객체의 경우 재사용해야 한다. 79 | - 서블릿컨테이너 : 서블릿을 실행시켜주는 역할 80 | - 서블릿 컨테이너에서 서블릿 인스턴스는 1개만 생성되고 이를 재사용하는 방식이다. 81 | - 서블릿 컨테이너는 모든 사용자 요청에 대해 Thread를 생성해야하나? 1000명이 요청하면 1000개의 Thread가 필요한가? 82 | - 서블릿 컨테이너는 서버가 생성할 수 있는 Thread수를 제한한다. 83 | - Thread Pool을 사용해 제한된 **Thread를 재사용**한다. 84 | - Thread수를 초과한 사용자들은 큐에서 대기하여 처리될때마다 Thread를 할당한다. 85 | - 그래서 Tomcat 설정에 maxThreads는 최대 Threads수, acceptCount는 큐의 최대 대기자수를 말한다. 86 | 87 | * Servlet과 JSP 시작 - 1 88 | - @WebServlet과 extends HttpServlet 되어 있는 class를 등록한다. 89 | 90 | ## 5일차 - MVC 프레임워크 1단계 구현 실습 91 | * 배움 관련 이야기 92 | - 주변사람과의 비교는 결국 나를 초라하게 만든다. 93 | * Servlet을 사용하여 MVC 프레임워크 구축하기 94 | - doGet, doPost를 사용하는 많은 Servlet Controller들을 Controller Interface 구현체로 만들어 리팩토링하기 95 | 96 | ## 6일차 - MVC 프레임워크 및 JDBC 97 | * MVC 프레임워크 Flow 98 | - 모든 요청은 중앙집중형 DispatcherServlet이 받는다. 요청에 대한 url을 RequestMapping에게 준다 99 | - RequestMapping은 url에 해당하는 Controller를 DispatcherServlet에게 전달해준다. 100 | - DispatcherServlet은 RequestMapping에게 받은 Controller를 실행시킨다. 101 | - J2EE 패턴중 Front Controller패턴이라고 보면 된다. (DispatcherServlet가 Front Controller라고 보면 된다.) 102 | - Controller은 사용자가 입력한 값에 대한 유효성 처리를 담당한다. 103 | - Controller은 Model과 View 사이를 연결하는 연결자 역할을 한다. 104 | - 비지니스 로직은 Model에서 구현해야 한다. 105 | - View는 Controller에게 받은 데이터를 출력시키기만 한다. 106 | 107 | * 서블릿 Life Cycle 108 | - init() : 서블릿 생성시 처리할 내용을 담고 있는 메소드 109 | - service() : 실제 수행될 로직을 담당 110 | - destroy() : 후처리 111 | - 컨테이너 : 해당하는 것(서블릿, 빈)들의 라이프사이클을 관리하는 역할 112 | 113 | * loadOnStartup 114 | - 서블릿컨테이너가 시작될때 해당 설정값을 가지고 있는 서블릿 인스턴스를 생성하고 init() 를 호출해준다. 115 | 116 | * JDBC 117 | - 문제점3 : 서버를 재시작하면 데이터가 사라진다. 영구보존 할수있는 방법을 도입해보자. 118 | - DAO는 DB, 외부API등 데이터 접근과 관련한 모든 처리를 담당한다. 119 | - JDBC는 인터페이스이다. 각 DBMS의 드라이버를 구현체로 사용하기때문에 DBMS 교체가 용이하다. 120 | 121 | ## 7일차 - Ajax 122 | * JDBC 코드리뷰 123 | - Object... parameters : 가변인자 (배열처럼 쓸수있으며 없어도 컴파일 에러가 없음. 단, 인자의 마지막에만 위치할 수 있음) 124 | - try-with-resources는 AutoCloseable을 구현한 객체일 경우에만 가능하다 125 | - web.xml의 welcome-file-list는 root path로 접근시 무조건 먼저 읽게 하는 설정이다. 이를 수정해야 원하는 페이지로 이동가능 126 | 127 | ## 8일차 - 중간점검 128 | 129 | 130 | ## 9일차 - AWS 배포 131 | * 프로그래밍을 잘한다의 정의 132 | - 현재 처해있는 상황에서 가장 적합한 기술을 찾아 적용할 수 있는 사람 133 | 134 | * Baas를 통해 백엔드 개발자의 자리가 점점 사라지는것 아닌가 135 | - 프론트엔드, QA, DBA, 시스템 등 다른 영역과 백엔드를 연결하는 접점에 대한 수요가 점점 생기고 있다. 136 | - 좋은 서비스를 개발하기 위해서는 결국 타 영역으로 넘어가야하는 순간이 온다. 137 | - 백엔드에 일정수준에 도달하면 타 영역에 도전해보자. 138 | 139 | * 신기술 공부? 140 | - 나만의 색깔 찾기 -> 일정시간 투자해 역량 쌓기 -> 다른 분야로 관심사 넓히기 141 | - 위 사이클을 반복하자 142 | - 가장 중요한건 내가 어떤것에 장점이 있고 나의 강점이 무엇인지 파악하기 143 | - 다른 사람이 뭐라해도 흔들리지 않는 자신만의 철학을 갖기 144 | 145 | * 중간 점검 피드백 146 | - 필터의 chain.doFilter()를 기준으로 전처리/후처리를 할수가 있음 147 | - MVC 프레임워크란 MVC를 제외한 나머지는 개발자가 개발하지 않아도 되는 프레임워크를 말한다. 148 | - stack vs heap (7번 문제) 149 | - 쓰레드마다 고유의 stack영역을 가지지만 Heap은 모든 쓰레드가 공유한다. 150 | - ShowController가 Heap영역에 있어, Question/answers 역시 Heap에 있고 이로인해 쓰레드가 요청시마다 필드인 Question/answers 가 유지가 안되서 문제가 발생한다. 151 | - Layered 아키텍처 : 여러개의 클래스 사이에 중복코드가 발생하면 Composition 패턴을 사용하는것이 좋다. 우리가 controller에서 service를 호출해 service에서 비즈니스로직을 위임하듯이 152 | 153 | * 실습 - AWS 배포 154 | 155 | ## 10일차 - Spring MVC 156 | * AWS 배포 리뷰 157 | - Symbolic link로 설정들을 잡아놓으면 이후 버전 업데이트시 변경양이 최소화됨 158 | - 웹서버들의 virtual host를 통해 동일 IP에서도 호스트에 따라 할당되는 프로젝트 다르게 처리가능 159 | 160 | * 스프링 MVC 161 | - 자세한 내용을 [Blog](http://jojoldu.tistory.com/28) 에 정리하였으니 참고하자. 162 | - POJO가 특정 클래스를 상속받지 않았는데도 Spring에서 관리되도록 할수 있는데 이는 해당 class를 Bean으로 관리되도록 설정할수있기 때문이다. 163 | - DispatcherServlet는 서블릿 컨테이너가 관리하지만, 확장포인트가 있어 Spring에서 설정 수정이 가능하다. 164 | - BeanFactory를 applicationContext가 상속하고 있다. 그래서 Bean에 관련한 기능들은 BeanFactory가 하고 있고 그외에 추가적인 기능들은 ApplicationContext에 추가되어있다. 165 | - 테스트 코드 작성을 예로 들면 166 | ``` 167 | ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 로 선언하면 Bean들을 다 사용할 수 있게 된다. 168 | ``` 169 | 170 | * @Bean의 @Component 차이는? [Blog](http://jojoldu.tistory.com/27) 참고! 171 | 172 | * web.xml없이 개발하기 173 | - 서블릿 3.0부터 지원하는 ServletContainerInitializer(WebApplicationInitializer)를 통해 가능하다. 174 | 175 | * MVC 프레임워크 만들기 176 | - 새로운 Request 요청은 new MockHttpServletRequest("POST", "/users") 식으로 만들수 있다. 177 | 178 | ## 11일차 - Spring MVC 실습 179 | * spring-web의 META-INF/services를 보면 ServletContainerInitializer에 풀네임의 initializer가 지정되어 있다. 180 | * 열어보면 WebApplicationInitializer 인터페이스를 구현한 모든 클래스들을 초기화 시키는 코드가 있음을 알수 있다. 181 | * MyWebInitializer에는 GET/POST 외에 다른 HTTP Method를 받을수 있도록 꼼수로 ``` ```로 지정하면 HiddenHttpMethodFilter 클래스가 처리해준다. 182 | * maven plugin에 downloadSources를 추가하면 이클립스에서 자동으로 jar를 attachment 해준다 (인텔리J는 자동으로 해줘서 신경안썼는데 유용할것같다!) 183 | * form의 input name은 model 객체의 field 이름과 매칭되는게 아니라 field의 set 메소드의 이름을 보고 자동으로 매칭을 시킨다. 184 | * Java Bean의 규약은 185 | - Default 생성자를 명시 (Controller에서 자동 매칭되는것은 디폴트 생성자를 통해서 하기 때문에) 186 | - getter/setter 메소드가 필수로 존재 187 | 188 | * 2가지 경우 중 하나는 꼭 해당되어야하는 if문을 작성할 경우 보통은 if/else로 잡지만 좀 더 안전하게 가려면 if/else if로 정상path를 잡고 else로 throw new Exception 한다. exception에는 404나 500 같은 status를 추가한다. 189 | 190 | ## 13일차 - Mockito를 활용한 테스트 191 | * class에서 상태를 가진다는게 무슨 뜻인지? 192 | - 상태 == 데이터 193 | - 메소드/함수 == 데이터(상태)를 변경 194 | - 상태 (데이터)를 관리하는 방법 195 | - 메소드의 인자를 활용 196 | - 클래스의 필드 상태값을 변경 가능하도록 구현 197 | - 클래스의 필드 상태값을 변경 불가능하도록 구현 198 | 199 | * static import : static 메소드들을 로컬 메소드처럼 메소드앞에 namespace 없이 바로 사용 가능하게 하는것 200 | * mockito 201 | - when : 어떤 메소드를 호출하면 202 | - thenReturn : 여기에 포함된 값을 리턴시킨다. 203 | - thenThrow : exception을 발생시킨다. 204 | - verify : 특정 객체가 몇번 호출 되면 등을 지정 205 | - @RunWith(MockitoJUnitRunner.class) TestClass : mockito 사용환경 구축 206 | - @Mock private UserDao userDao : userDao의 가짜 구현체를 생성 207 | - @InjectMocks private UserService userService : 내가 지정한(@Mock) 을 해당 Bean의 @Autowired 객체에 대신하고 싶다. 208 | 209 | * Junit 210 | - @Test(expected = 원하는 Exception.class) : 해당 메소드는 실행시 지정한 Exception.class가 무조건 발생한다로 비교 211 | 212 | * 객체지향적 코딩 213 | - 객체지향적인 코딩이란 상태를 가지고 있는 객체가 상태를 변경하도록 코딩을 해야한다. 214 | - 코드가 객체지향적이라면 mockito와 같은 테스트 프레임워크가 없더라도 테스트코드를 작성할수가 있다. 215 | - service가 비지니스로직의 모든것을 처리하진 않는다. 상태를 가지는 객체가 로직을 처리하는것이 맞다. 216 | - service는 도메인 객체들간의 관계를 정리하고, 캐시/트랜잭션들에 대한 처리를 할뿐이다. 217 | 218 | * 빈 컨테이너 설정 관리 219 | - 모든 Layer의 설정을 하나에서 관리 vs Layer별로 설정을 분리 220 | - excludeFilters에 Controller.class를 지정해서 @ComponentScan에서 제외시키겠다. 221 | - includeFilters에 Controller.class를 지정해서 해당 어노테이션만 스캔하겠다. 222 | - 부모/자식 구조 : 프레젠테이션 Layer(Controller)만 다르고 Service/Dao설정을 재사용 223 | 224 | * 인스턴스 추가/삭제 225 | - @PostConstruct : Bean이 생성되고 DI후, 바로 실행할 메소드 지정 226 | - @PreDestroy : Bean이 삭제되기 바로 직전에 실행할 메소드 지정 227 | 228 | * Property와 환경변수 229 | - reload resource bundle을 통해 주기적으로 properties를 체크해서 적용하도록 할수는 있다. 230 | - 변경이 있으면 안되는 DB설정 같은 경우는 제외하고, 자주 변경이 필요한 properties만 reload 설정에 포함시킨다. (설정간 분리가능) 231 | 232 | ## 14일차 - AOP 233 | * [AOP정리 참고](http://jojoldu.tistory.com/69) 234 | * properties는 한글을 유니코드로 관리하는게 디폴트다. 235 | * messageSource로 properties의 값을 몇초 주기로 리로딩할수 있다. 236 | ![messageSource config code](./images/messageSource.png) 237 | 238 | * AOP가 필요한 요구사항 239 | - 모든 Controller와 Dao에서 처리시간이 500ms 이상인 경우는 로그를 남겨라 240 | - Dao 클래스의 모든 method에 인자로 전달되는 인자를 debug 레벨로 로그를 남겨라 241 | 242 | * AOP가 해결하고자 하는것은 비지니스로직의 중복제거가 아닌 시스템 전반적인 중복을 제거하기 위함 243 | - 비지니스 로직 코드의 중복 제거 : OOP 244 | - 인프라 코드의 중복 제거 : AOP 245 | 246 | * FactoryBean : new 키워드를 통해 생성될수 없는 Bean들을 생성하기 위해 필요 247 | - 예를 들어, 기존 Bean에 몇가지 기능이 추가된 ProxyBean이 필요할때 FactoryBean을 implement하여 빈을 등록한다 248 | - 예전 xml로 Bean을 관리하던 시절에는 FactoryBean의 구현이 필요했지만 현재는 java code로 Bean등록이 가능하므로 현재는 잘쓰이지 않는다. 249 | 250 | * 용어 251 | - target : 부가기능을 부여할 대상 252 | - advice : target에 제공할 부가 기능을 담은 클래스 253 | - joinpoint : sadvice가 적용될 수 있는 위치, 예를 들어 method의 실행단계 254 | - pointcut : joinpoint를 선별하는 작업 또는 그 기능을 정의한 모듈 255 | 256 | ## 15일차 - Transactional & Cache & SpringBoot 257 | #### Transactional 258 | * readOnly = true : select성 메소드에서는 성능이 더 좋아서 추천 259 | * isolation level : 하나의 데이터를 반영하는데 동시에 2명이상이 접근했을때 데이터를 어떻게 관리하는지에 대한 개념 260 | - tx1에선 select, tx2에선 update를 동시에 요청하면? isolation level마다 다르다. update후 select할지, select하고 update할지 261 | * isolation level의 레벨이 낮을수록 성능은 좋고 데이터 무결성 보장성을 낮춘다. 262 | 263 | * propagation : tx 생성 rule 264 | * propagation_required : default이며, 상위계층(service)에서 하위 계층(dao)요청시 하위계층에 tx가 없으면 **상위계층의 tx를 사용**한다 265 | * propagation_new : 상위계층(service)에서 하위 계층(dao)요청시 하위계층에 tx가 없으면 **새로운 tx를 만든다** 266 | * Spring transactional은 runtime exception일 경우에만 rollback되고 checked exception에서는 rollback되지 않는다 267 | - rollbackFor = checked Exception을 지정하여 사용할 수 있다. 268 | * 테스트 코드 작성시 Dao일 경우에는 테스트 후 DB를 원복시키고 싶을경우가 많으니 class 혹은 method에 @Transactional을 지정하면 된다. 269 | 270 | #### Cache 271 | * @Cacheable(key="키값") : 해당하는 키값을 캐시하도록 지정 272 | * @CacheEvict(key="키값") : 해당하는 키값이 변경되면 캐시데이터를 변경하도록 한다 273 | * 직접 만든 캐시를 해당 어노테이션으로 관리하고자 한다면 CacheManager 구현체를 만들고 지정하면 된다 274 | 275 | ## 16일차 - JPA 소개 276 | * 설계 패러다임의 변경: Table -> Object 277 | * 대세는 쿼리매퍼에서 ORM으로 넘어가고 있다. (C#, 루비등 타언어에선 이미 ORM이 대세) 278 | * 하이버네이트와 달리 SpringDataJpa는 interface의 메소드 네이밍으로 조건절 생성 279 | * @Transient : DB 컬럼과 맵핑되는것에서 제외시킨다. 280 | * @ManyToOne을 지정했다고해서 반대편 Entity에 꼭 OneToMany를 할필요는 없다. 281 | 282 | ## 17일차 - JPA + Profile 283 | * @MappedSuperclass : 284 | ![MappedSuperclass](./images/mappedsuperclass.png) 285 | 286 | * @JpaTest (SpringBoot 1.4부터 지원) 287 | * 객체지향적인 코딩이란 객체들끼리 서로 협력해서 일을 처리하도록 하는것 288 | - 만약 서비스 메소드에서 직접 필요한 객체를 가져오는 일을 다 하고 있다면 절차지향적인 방법일 확률이 높다 289 | 290 | * API 서버 구축 도구 291 | - [Swaggger2](http://www.baeldung.com/swagger-2-documentation-for-spring-rest-api) : API를 문서화할수 있는 도구 [상세 내용](http://jojoldu.tistory.com/31) 292 | - restTemplate & @SpringBootTest 293 | 294 | ![RestTemplate&SpringBootTest](./images/resttemplate.png) 295 | 296 | * Flyway DB 297 | - 테이블 스키마의 버전관리 (DB계의 Git이라 보면 됨) 298 | - 네이밍 규칙: V숫자__설명 299 | - ex) V1__init_db_schema.sql 300 | - Spring Boot 설정 파일에 spring.jpa.show-sql=true로 설정되어 있으면 서버가 시작할 때 생성되는 테이블 스키마, foreign key 생성 쿼리를 확인할 수 있다. 301 | 302 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | REPOSITORY_DIR=/home/git/jwp-basic 3 | PID="ps -ef|grep tomcat|grep java| awk ' { print $2 } '" 4 | cd $REPOSITORY_DIR 5 | sudo git pull 6 | sudo mvn clean package -Dmaven.test.skip=true 7 | kill -9 $PID 8 | mv $REPOSITORY_DIR/target/jwp-basic /usr/local/tomcat8/webapps/ROOT 9 | startup 10 | -------------------------------------------------------------------------------- /images/fastcampus-java-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/images/fastcampus-java-1.jpg -------------------------------------------------------------------------------- /images/mappedsuperclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/images/mappedsuperclass.png -------------------------------------------------------------------------------- /images/messageSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/images/messageSource.png -------------------------------------------------------------------------------- /images/resttemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/images/resttemplate.png -------------------------------------------------------------------------------- /out/production/fastcampus-java/main/Calculator.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/out/production/fastcampus-java/main/Calculator.class -------------------------------------------------------------------------------- /out/production/fastcampus-java/main/StringCalculator.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/out/production/fastcampus-java/main/StringCalculator.class -------------------------------------------------------------------------------- /out/production/fastcampus-java/test/main/CalculatorTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/out/production/fastcampus-java/test/main/CalculatorTest.class -------------------------------------------------------------------------------- /out/production/fastcampus-java/test/main/StringCalculatorTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jojoldu/fastcampus-java/33653fc622d9c61c2398a90dbd9e5e44dbb9a7b1/out/production/fastcampus-java/test/main/StringCalculatorTest.class -------------------------------------------------------------------------------- /review/README.md: -------------------------------------------------------------------------------- 1 | # 패스트캠퍼스 자바지기님의 강의 수강을 마치며 2 | 3 | 2016.06.27 ~ 2016.08.25 총 8.5주라는 시간동안 [자바지기님의 강의](http://www.fastcampus.co.kr/dev_camp_jwp/)를 수강하였다.
4 | * 강의는 얼마나 좋았는가? 5 | - 얼리버드라서 할인을 받았지만 120만원(기본은 145만원)이란 금액을 지불하였다. (내돈으로.. 회사돈 아님) 6 | - 근데 이번 강의의 다음 스탭에 해당하는 강의가 개설되면 또 들을 생각이다. 내돈으로 7 | - 8주에 120~150만원 이란 금액이 작은 금액은 아니다. 근데 **또 들어야겠다고 생각이 들정도로 좋은 강의**였다. 8 | 9 | 강의 내용에 대해선 [Github](https://github.com/jojoldu/fastcampus-java)에 정리 하였으니 참고하면 좋을것 같다.
10 | (아직도 작성중이다. 수업내용을 복기하는 마음으로 다시 보면서 내용을 추가/수정중이다.) 11 | * 다른 회사의 개발방식을 알수 있었다. 12 | - 우리회사도 SpringBoot와 ORM을 사용중이다 (SpringDataJpa가 아닌 네이티브 하이버네이트이지만...) 13 | - 하지만 TDD를 진행하지 않고 있으며, 14 | - 개인적으로 **가장 좋았던 부분**이다. 15 | 16 | * 맞춤형 강의였다. 17 | - 2주정도 지난 시점에서 수강생들의 실력이 비슷하지 않다는것을 인지하시고, 강의 내용을 2가지 타입으로 나누셨다. 18 | - A : SpringMVC 이미 사용해본 수강생들에겐 직접 MVC/DI 프레임워크를 구축하는 과정을 진행 19 | - B : SpringMVC 혹은 웹개발을 처음 접한 수강생들에겐 SpringMVC를 사용한 웹개발 과정을 진행 20 | - 각자 실력에 맞게 A/B를 나눠서 동시 진행하다보니 너무 어려워하거나 쉬워하는 수강생들이 줄어 끝까지 텐션을 유지할 수 있었다. 21 | 22 | * 우리가 사용중인 프레임워크를 직접 구현해보았다. 23 | - 톰캣도 없고, Spring도 없는 상황에서 순수 Servlet 만으로 게시판을 구현해보는 경험은 절대 이 시간외에는 경험해보지 못했을 것이다. 24 | - HttpRequest가 쓰레드를 상속 받는것을 이제야 알았다. 25 | - JDBC Template를 직접 구현해보았다. (난 시간이 되서 ORM도 도전해보았다.) 26 | - 27 | 28 | * TDD를 시작할수 있게 되었다. 29 | - 사내 우리팀에서 TDD가 활성화 되어 있지 않아(타팀은 하는지 잘모르겠어서..), 온라인상에서 아무리 TDD의 중요성을 외쳐도 실감하지 못하고 있었다. 30 | - 책이나 동영상 강의로 혼자 공부하기는 어려웠다. 뜬구름잡는 이야기도 많았고, 웹 프로젝트보다는 Java 어플리케이션을 예제코드로 보여줘서 Bean Injection, DB연동, HttpRequest 등에 대한 테스트를 알수없었다. 31 | - 강의에서 Junit & Mockito같은 테스트 프레임워크 없이 테스트 코드 32 | - 블로그에 기술관련 포스팅을 할 경우 항상 테스트 코드를 통해 코드를 공개하게 되었다. 33 | - 회사에서 웬만큼 일정이 급하지 않은 경우에 테스트 코드를 작성하면서 개발을 진행하게 되었다. 34 | 35 | * 객체지향적 코딩에 대해 36 | - 37 | -------------------------------------------------------------------------------- /src/main/week1/Calculator.java: -------------------------------------------------------------------------------- 1 | package main.week1; 2 | 3 | /** 4 | * Created by jojoldu@gmail.com on 2016-06-27. 5 | */ 6 | public class Calculator { 7 | public int add(int a, int b){ 8 | return a+b; 9 | } 10 | 11 | public int minus(int a, int b){ 12 | return a-b; 13 | } 14 | 15 | public int divide(int a, int b) { 16 | return a/b; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/week1/StringCalculator.java: -------------------------------------------------------------------------------- 1 | package main.week1; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * Created by jojoldu@gmail.com on 2016-06-27. 8 | */ 9 | public class StringCalculator { 10 | public int add(String str){ 11 | if (isBlank(str)) return 0; 12 | 13 | return sum(getStrArray(str, getSplitChar(str))); 14 | } 15 | 16 | private boolean isBlank(String str) { 17 | return str == null || str.isEmpty(); 18 | } 19 | 20 | private int sum(String[] strs) { 21 | int result = 0; 22 | for(String s : strs){ 23 | result += str2int(s); 24 | } 25 | return result; 26 | } 27 | 28 | public String getSplitChar(String str){ 29 | String defaultSplit = ",|:"; 30 | String regex = "\\/\\/(.*?)\\\n"; 31 | Pattern p = Pattern.compile(regex); 32 | Matcher m = p.matcher(str); 33 | if(!m.find()){ 34 | return defaultSplit; 35 | } 36 | return m.group(1); 37 | } 38 | 39 | public String[] getStrArray(String str, String split){ 40 | String[] arr = str.split(split); 41 | if(isNotNumber(arr[0]) && isNotNumber(arr[1])){ 42 | arr[0] = "0"; 43 | arr[1] = arr[1].replaceAll("\\\n", ""); 44 | } 45 | return arr; 46 | } 47 | 48 | private boolean isNotNumber(String str){ 49 | try { 50 | Integer.parseInt(str); 51 | return false; 52 | } catch (NumberFormatException e) { 53 | return true; 54 | } 55 | } 56 | 57 | private int str2int(String str){ 58 | try { 59 | int result = Integer.parseInt(str); 60 | if(result < 0){ 61 | throw new RuntimeException(); 62 | } 63 | return result; 64 | } catch (NumberFormatException e) { 65 | return 0; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/main/week1/CalculatorTest.java: -------------------------------------------------------------------------------- 1 | package test.main.week1; 2 | 3 | import main.week1.Calculator; 4 | import org.junit.Test; 5 | import org.junit.Before; 6 | import org.junit.After; 7 | import static org.junit.Assert.*; 8 | 9 | /** 10 | * Calculator Tester. 11 | * 12 | * @author 13 | * @since
6�� 27, 2016
14 | * @version 1.0 15 | */ 16 | public class CalculatorTest { 17 | 18 | private Calculator calculator; 19 | 20 | @Before 21 | public void before() throws Exception { 22 | calculator = new Calculator(); 23 | } 24 | 25 | @After 26 | public void after() throws Exception { 27 | } 28 | 29 | @Test 30 | public void testAdd() throws Exception { 31 | assertEquals(calculator.add(1,2),3); 32 | } 33 | 34 | @Test 35 | public void testMinus() throws Exception { 36 | assertEquals(calculator.minus(2,1),1); 37 | } 38 | 39 | @Test 40 | public void testDivide() throws Exception { 41 | assertEquals(calculator.divide(2,1), 2); 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/main/week1/StringCalculatorTest.java: -------------------------------------------------------------------------------- 1 | package test.main.week1; 2 | 3 | import main.week1.StringCalculator; 4 | import org.junit.Test; 5 | import org.junit.Before; 6 | import org.junit.After; 7 | import static org.junit.Assert.*; 8 | 9 | /** 10 | * StringCalculator Tester. 11 | * 12 | * @author 13 | * @since
6�� 27, 2016
14 | * @version 1.0 15 | */ 16 | public class StringCalculatorTest { 17 | 18 | private StringCalculator stringCalculator; 19 | 20 | @Before 21 | public void before() throws Exception { 22 | stringCalculator = new StringCalculator(); 23 | } 24 | 25 | @After 26 | public void after() throws Exception { 27 | } 28 | 29 | /** 30 | * 31 | * Method: add(String str) 32 | * 33 | */ 34 | @Test 35 | public void testAdd() throws Exception { 36 | String str = "1,2:4"; 37 | assertEquals(stringCalculator.add(str), 7); 38 | } 39 | 40 | @Test 41 | public void testAdd1() throws Exception { 42 | String str = null; 43 | assertEquals(stringCalculator.add(str), 0); 44 | } 45 | 46 | @Test 47 | public void testAdd2() throws Exception { 48 | String str = ""; 49 | assertEquals(stringCalculator.add(str), 0); 50 | } 51 | 52 | @Test 53 | public void testAdd3() throws Exception { 54 | String str = "//;\n1;2;3"; 55 | assertEquals(stringCalculator.add(str), 6); 56 | } 57 | 58 | @Test 59 | public void testAdd4() throws Exception { 60 | String str = "//;;\n1;;2;;3"; 61 | assertEquals(stringCalculator.add(str), 6); 62 | } 63 | } 64 | --------------------------------------------------------------------------------