
자바 9부터 자바 21까지
₩55,000
초급 / Java
5.0
(26)
자바 9부터 자바 21까지 주요 업데이트 내용을 상세하게 다룹니다. 자바 31까지 최신 버전이 나오면 지속적으로 업데이트 됩니다.
초급
Java
안녕하세요 😊
제가 아는 내용을 이해하기 쉽고, 재미있게 설명드려 여러분들이 성장하실 때 행복함을 느끼는 개발자 최태현입니다.
(현) 캐치테이블[와드] 소프트웨어 엔지니어
(전) 스타트업 소프트웨어 엔지니어 리드
(전) 배달의민족[우아한형제들] 소프트웨어 엔지니어
(교육활동) Next Step 리뷰어 다수 참여, 공기관 & 스타트업 경진대회 강사 및 멘토, 스파르타 코딩클럽 멘토
한국과학기술원 (KAIST) 졸업
자바 9부터 자바 21까지
₩55,000
초급 / Java
5.0
(26)
자바 9부터 자바 21까지 주요 업데이트 내용을 상세하게 다룹니다. 자바 31까지 최신 버전이 나오면 지속적으로 업데이트 됩니다.
초급
Java
2시간으로 끝내는 코루틴
₩16,500
초급 / Kotlin, coroutine
5.0
(48)
비동기 프로그래밍의 필수 라이브러리 코루틴! 코루틴의 개념, 사용법, 그리고 내부 원리까지 한 번에 얻어가세요!
초급
Kotlin, coroutine
코틀린 고급편
₩55,000
중급이상 / Kotlin, 객체지향, 함수형 프로그래밍
4.9
(43)
코틀린의 모든 언어적 특성을 이해할 수 있습니다. 강의를 들으신 후 제네릭, 위임과 지연, DSL과 리플렉션 등 코틀린 고급 기술을 활용해 마음껏 프로그래밍하실 수 있습니다.
중급이상
Kotlin, 객체지향, 함수형 프로그래밍
자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
₩132,000
초급 / Java, Spring Boot, Spring, JPA, AWS, MySQL
4.9
(116)
Java와 Spring Boot, JPA, MySQL, AWS를 이용해 서버를 개발하고 배포합니다. 웹 애플리케이션을 개발하며 서버 개발에 필요한 배경지식과 이론, 다양한 기술들을 모두 학습할 뿐 아니라, 다양한 옵션들의 의미, Spring Boot의 역사와 가장 최신 버전의 변경점들도 짚어봅니다.
초급
Java, Spring Boot, Spring
실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
₩77,000
초급 / Kotlin, Spring Boot, Java, 리팩토링, Spring
5.0
(118)
Java + Spring Boot로 만들어진 웹 애플리케이션을 Kotlin + Spring Boot로 리팩토링 하고 추가 요구사항을 구현합니다. 이 과정에서 Junit5, SQL, JPA, Querydsl 을 사용할 뿐 아니라, 설계 및 구현 관점에서 다양한 방법의 장단점을 다루게 됩니다.
초급
Kotlin, Spring Boot, Java
자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)
₩55,000
초급 / Kotlin, Java, 객체지향
5.0
(213)
이 강의를 통해 Kotlin 언어의 특성과 배경, 문법과 동작 원리, 사용 용례, Java와 Kotlin을 함께 사용할 때에 주의할 점 등을 배울 수 있습니다.
초급
Kotlin, Java, 객체지향
질문&답변
선생님 강의를 듣고 크롤링에 코루틴을 적용해보고 있습니다. 그런데 코루틴이 하나만 나와서 동시처리가 안되는데 혹시 봐주실 수 있나요??
안녕하세요 재익님! 🙂 질문 주셔서 감사합니다. 결론부터 말씀드리면, 현재 blocking I/O가 single thread를 점유하고 있기 때문에 그런 것으로 보입니다.bookLinks.mapIndexed { i, link -> businessLogic() }에서 businessLogic() 은 크게외부 Page를 불러오는 과정불러온 Page에 대한 데이터를 가공하는 과정으로 나눠지는 것 같은데요! 이 중 1번은 blocking I/O로 코루틴 안에서 실행한다고 하더라도, 코루틴도 결국 특정한 스레드에 매핑되어 실행 (1강 내용입니다!) 되기 때문에 스레드가 하나인 경우 로딩 -> 로직 실행 -> 로딩 -> 로직 실행 처럼 순차적인 결과로 보일 수 밖에 없습니다. 가장 간단한 해결책은 해당 로직을 여러 스레드에 매핑될 수 있게끔 해주는건데요!예를 들어 스레드가 5개라면, 동시에 5명이로딩 -> 로직 실행로딩 -> 로직 실행로딩 -> 로직 실행로딩 -> 로직 실행로딩 -> 로직 실행을 하기 때문에 로딩이 blocking 하게 3초 정도 걸린다 하더라도 3초에 5번씩 businessLogic을 돌릴 수 있게 됩니다.runBlocking { val jobs = bookLinks.mapIndexed { i, link -> async(Dispatchers.IO) { businessLogic() } } jobs.awaitAll() }처럼 runBlocking 만 사용해 '하나의 스레드'에 코루틴을 배정하지 않고 Dispatchers.IO 를 통해 여러개의 스레드에 코루틴을 배정하면 원하시는 결과를 얻으실 수 있을거에요! 🙂 또 다른 해결책으로는 blocking I/O를 non-blocking I/O로 교체하는건데, 이는 저희가 100% 제어할 수는 없고 non-blocking I/O가 지원되는 라이브러리가 있는지 확인이 필요하기에 애매할 수 있습니다. 학습하신 내용을 바로 적용해 보려 하시다니 대단하시네요~! 👍 답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇
질문&답변
nohup.out 관련 문제
안녕하세요 태진님! 🙂 질문 감사합니다. Error: Unable to access jarfile library-app_complete/build/libs/library-app-0.0.1-SNAPSHOT.jar현재 nohup.out 파일을 열었더니 이렇게 기록이 남아 있다는 말씀이시죠~ 우선 잘 알고 계시겠지만,nohup java -jar library-app_complete/build/libs/library-app-0.1-SNAPSHOT.jar --spring.profiles.active=dev &라는 명령어는 단지 java -jar library-app_complete/build/libs/library-app-0.1-SNAPSHOT.jar --spring.profiles.active=dev라는 명령어를 "백그라운드"로 실행하고 출력을 nohup.out 이라는 파일에 전달할 뿐입니다. 따라서 nohup.out 에 에러 로그가 있다면 우리가 실행하려 했던 java -jar ... 명령어에서 에러가 났다는 의미와 같은데요,에러 로그를 확인해보면 library-app_complete/build/libs/library-app-0.0.1-SNAPSHOT.jar 파일을 현재 찾을 수 없다고 되어 있습니다. 작성해주신 경로는 '상대' 경로이기 때문에 해당 경로가 올바르게 되어 있는지 jar 파일 경로를 한 번더 확인해보시면 좋을 것 같아요!A/ B/ C/ d.out이라는 구조가 있을 때제가 A 폴더 안에서 C/d.out 이라 하면 올바른 경로이지만, C 안에서 C/d.out 이라 하면 올바르지 않은 경로가 됩니다. 답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇
질문&답변
안녕하세요! 질문이 있습니다
안녕하세요 지웅님! 🙂 아유~ 언제든 질문 주셔도 괜찮죠..! 정말 편하게 질문 남겨주세요."빌드할 때 동기화 부분에서 실패합니다. 근데 이게 어떤 오류 메시지도 나오지 않고 저렇게 실패라고만 나와서 원인을 알기 어렵습니다." 라고 말씀해주셨는데, 이런 경우라면 저도 찾기 어렵군요! 🥲 에러를 특정하기 어렵다면 저도 아래 AI 인턴이 말해준 것처럼 일반적인 답변 외에 더 드리기는 어렵습니다. 어딘가 에러 로그가 숨겨져 있을 수도 있긴한데... https://namocom.tistory.com/777를 참고하셔서 실행 환경을 gradle or IntelliJ로 적절히 변환해서 어떤 로그가 나오는지 확인하며 추적하는 것도 방법일 것 같습니다. (저는 개인적으로 gradle 실행/테스트 환경을 쓰고 있어요) 추가로 본 강의를 따라하실 때 제공되는 프로젝트를 기반으로 진행하시는 것을 추천드립니다. 웹 UI가 기본적으로 제공되는 것을 활용하시려면 제공되는 프로젝트를 사용하셔야 하거든요! 물론 제공되는 프로젝트에서 웹 UI만 가져가셔도 괜찮습니다. ☺ 혹시나 추가적으로 발견한 내용이 있으시면 저도 더 찾아보겠습니다. 감사합니다. 🙇
질문&답변
7강에 대해서 궁금증이 있는데요
안녕하세요~ drrg9211님! 🙂 하나씩 답변 드려 보겠습니다. [1. 코루틴 스코프를 새로 만드는 것은 효과]같은 Dispatcher를 사용했을 때 코루틴 안에서 다시 한 번 새로운 코루틴 Scope를 만든다면... 사실상 무언가 효과를 기대하기는 어렵다고 생각됩니다.결국 코루틴 Scope는 코루틴을 구성하는 여러 요소 (Context) 를 모아둔 영역인데, 별다른 Context의 변경 없이 새로운 Scope을 만든다고 해서 딱히 특별할 건 없거든요!제 개인적으로도 코루틴을 사용하며 코루틴 Scope을 말씀해주신 방식으로는 사용해보지 않은 것 같습니다. [2. 상위 코루틴에서 하위 코루틴을 만들 때]이 때도 비슷합니다! 사실 새로운 CoroutineScope를 만드는 것은 자식 코루틴을 만드는 것과 유사한데요, 결국 Job 으로 표현되는 하나의 코루틴도 CoroutineContext의 구성 요소이고 자식 코루틴을 만드나 새로운 Scope을 만들며 자식 코투린을 만드나 결과적으로는 동일합니다.저 역시도 CoroutineScope 은 코루틴을 사용하지 않는 환경에서 코루틴을 사용하는 환경을 넘어갈 때만 명시적으로 사용해본 경험이 있습니다. 🙂 답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇
질문&답변
Spring MVC에서 corountine 활용 방안
안녕하세요~ yki1204님! 정말 좋은 질문 감사드립니다. 저도 Learning curve가 높은 webflux 보다는 MVC를 선호해서 오랫동안 고민하던 부분이네요 🙂 본격적으로 질문에 대한 답변을 드리기에 앞서, 제가 생각하는 코루틴의 최강 장점은 비동기 프로그래밍을 마치 동기 프로그래밍 처럼 하게 해준다 라는 것입니다.말씀해주신 것처럼 CompletableFuture 를 적절히 활용하면 Java 에서도 충분히 비동기 프로그래밍을 할 수 있습니다. 하지만, 비동기 프로그래밍의 특성상깊어지는 callback depth직관적인지 않은 중첩 비동기 호출CompletableFuture 에 대한 약간의 코드 의존성과 불편함runAsync, supplyAsync 와 그에 맞춰 들어가는 함수형 프로그래밍 등등이라는 단점이 일부 있다고 (개인적으로) 생각하는데요코루틴은 이를 중단 함수라는 개념을 활용해 비동기 프로그래밍 인데도, 마치 동기 프로그래밍을 작성하는 것과 같은 코드 가독성을 제공한다고 생각합니다. 🙂 이런 관점에서 하나씩 답변 드려 보겠습니다.Spring MVC 구조에서 block 기반의 로직 처리 시 CoroutineScope(Dispatchers.Default).async { } 으로 감싸서 호출하는 구조가 coroutine의 장점을 활용한 방식이 맞을까요?네 저는 장점이라고 생각합니다. 더 정확히는 Spring MVC는 Spring 5.3.x 부터 코루틴과의 통합을 지원하기에 Controller Level 부터 suspend 함수를 사용하실 수 있습니다.@GetMapping("/api/v1/user") suspend fun getUser() { // 즉, 여기에 자유롭게 코루틴을 만들어 낼 수 있죠 }runBlocking을 사용하서도 되고 endpoint 부터 suspend 함수를 쓰셔도 된다고 생각합니다.@GetMapping("/api/v1/user") fun getUser() = runBlocking { }그리고 제가 생각하기에 "코루틴이 비동기 프로그래밍 코드를 직관적이고 쉽게 만들어 준다" 라는 측면에서 외부 I/O를 여럿 호출해야 하는 비동기 프로그래밍에 코루틴을 사용한다면 이는 코루틴을 충분히 활용하는 예시라고 생각합니다. 단, 예시로 적어주신 것처럼 RestTemplate 혹은 spring JPA의 기본 Repository은 모두 blocking I/O 입니다. 따라서 코루틴이 특정 스레드에 실행되어 I/O를 태우게 되면 해당 스레드는 blocking이 걸리죠.그래서 저는 스프링 MVC + 코틀린 조합에선 "외부 API를 다수 사용해야 할 때 webClient만 얹어 쓰는" 편입니다. DB 같은 경우는 기존 MVC 패턴을 그대로 쓰고 있습니다. 🙂 1번이 장점이 아니라면 non-blocking으로 처리 가능한 webclient나 ktorClient을 사용해야 해야 할까요?말씀 드렸던 것처럼 저는 충분히 장점이라고 생각합니다. 비록 RestTemplate을 사용해 blocking이 일어난다고 하더라도 병렬 비동기 프로그래밍을 쉽게 할 수 있다는 측면이 긍정적으로 느껴져요.다만, RestTemplate은 한 때 deprecated 될 뻔 하기도 했고,관련 블로그 : https://poalim.tistory.com/59스프링 부트 3부터는 declarative HTTP Client + WebClient 조합도 무척 강력하기 때문에관련 블로그 : https://junhyunny.github.io/spring-boot/declarative-http-client-in-spring-boot/저는 WebClient 사용을 추천(?) 드리긴 합니다 ☺ 보통 Spring에서 coroutine을 활용하려면 webflux를 사용하는게 기본일지? 혹시 다른 활용 방안은 없을지?마지막으로 MVC와 함께 라면 위에 말씀드린 것처럼 활용이 제한적인 측면이 있습니다. 결국 외부 I/O 프로그래밍 중 DB는 R2DBC를 쓰지 않는 이상 비동기 호출이 어려우니 API 호출 정도로 제한되죠.하지만 서비스가 커지고 독립적인 서버가 여럿 뜨게 되면 생각보다 이 장점이 크게 다가올 수 있고, 서버 개발자 입장에서는 API만 개발하는게 아니라 pub-sub 패턴, 배치, 스케줄링 같은 다양한 로직을 개발하게 되는데, 이럴 때 코루틴이 적용 가능한 부분도 꽤 있었습니다. 🙂예를 들어 kafka event를 받아, 독립적인 또 다른 서버의 API를 수십개 호출한 결과를 조립해 어딘가에 적재해야 한다거나.. (전형적인 zero-payload CQRS 패턴이죠) 스케줄링의 성능을 당장 높이기 위해 모든 로직을 그대로 두고 Dispatcher 스레드 풀만 설정을 추가해 cpu core 활용을 끌어 올려 성능을 N배 개선한다거나.. 하는 식으로 활용이 가능했던 것 같습니다. 답변이 도움이 되었으면 좋겠습니다.감사합니다. 🙇
질문&답변
abstract class Fish(name:String):Animal(name)
안녕하세요~! nayun oh님! ☺ 질문 주셔서 감사합니다!코틀린에서는 하위 클래스에서 상위 클래스에 있는 필드와 동일한 이름으로 다시 선언을 하려 하면 명시적으로 override를 해줘야 합니다!val을 붙이지 않으면 단순히 name을 받아 전달해 줄 뿐이지만, val을 붙인다는 것은 클래스의 필드로 선언한다는 의미라 val을 붙일 수 없습니다.답변이 도움이 되었으면 좋겠습니다~ 추가로 궁금한 내용 있으시면 편하게 질문주세요! 감사합니다. 🙇
질문&답변
9강 코루틴 중단과 재개관련 문의 드립니다.
안녕하세요 태혁님! 🙂 좋은 질문 감사합니다. 잘 아시는 것처럼 suspend 함수는 중단될 '수도' 있는 지점입니다. 즉 바꿔 말하면 중단되지 않을 수도 있다는 의미이고, 실제 중단이 될지, 중단이 되지 않을지는 내부의 구현에 따라 달려 있습니다.예를 들어 delay(1_000L) 는 실제 1초 후에 다음 지점에서 재개해야 하므로 중단이 일어나는 suspend 함수인건데요,delay의 구현을 보면public suspend fun delay(timeMillis: Long) { if (timeMillis -> // if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule. if (timeMillis 와 같은 코드를 확인할 수 있고, 여기서 실질적으로 scheduleResumeAfterDelay 이란 함수가 호출되며 중단이 일어나게 됩니다. 다시 scheduleResumeAfterDelay 라는 함수는 Dispatchers 와 같이 코루틴의 스케줄링을 담당하는 클래스에 구현이 되어 있는데 이를 바꿔 표현하면 코루틴 스케줄러에게 중단 신호를 보내면 스케줄러 구현에 따라 직접 중단을 한다고 할 수 있습니다.물론 이 과정에서 중단이 된 Job (= Coroutine)은 Context 같은 곳에 상태를 변경하게 (isActive=false로 바꾼다거나요~) 될거에요! 🙂 제가 모든 중단 로직을 확인해본 것은 아니지만, 결국 실제 중단을 일으키는 함수의 구현에 따라 조금씩 다를 수 있지 않을까 싶습니다. 답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇
질문&답변
테스트 후 AfterEach 함수에서 나오는 쿼리
안녕하세요! ysko님! ☺ 좋은 질문 감사합니다.하나씩 답변 드려 보겠습니다. [1. deleteAll 이전에 나오는 쿼리도 N+1처럼 동작하는지]네 비슷합니다.보다 정확하게는 deleteAll 을 호출하게 되면, select * from t; 를 통해 삭제할 Entity를 모두 가져오게 되고, 혹시나 연관관계가 있는 테이블이 있다면 해당 테이블도 삭제하기 위해 N+1 처럼 N 번 select 쿼리를 실행하게 됩니다.(사진)이런 동작 방식 대신 deleteAllInBatch 를 사용하실 수도 있습니다.deleteAllInBatch 는 select 쿼리 없이 모든 데이터를 지우는데요, 따라서 자동 생성된 FK를 피하기 위해 1 : N 구조에서 N(자식) 쪽 테이블부터 deleteAllInBatch 를 이용해 모든 데이터를 제거하고, 부모 쪽 테이블을 지우는 식으로 구현해야 합니다. [2. 테스트 코드이므로 테스트 객체의 모수가 작아 무시해도 되는지]넵 맞습니다! 👍 제 개인적인 생각으로는 테스트이기도 하고, 모수가 그렇게 크지 않아서 괜찮을 것 같습니다.혹시나 모수가 굉장히 많아지는 데이터를 테스트 하시게 된다면 위에서 말씀드린 deleteAllInBatch 방식을 쓰시는 것도 괜찮습니다. 답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇
질문&답변
mysql 연결관련 에러 질문있습니다
안녕하세요 준호님~ ☺ AI 인턴이 잘 이야기 해주겠네요~ driver를 찾을 수 없는 상황에서는 의존성 설정을 한 번 확인해보시면 좋을 것 같아요!다른 질문글에서는 강의 설정과 조금 다르게 변경했더니 오히려 잘 된다고 하시는 분도 계시더라고요!dependencies { runtimeOnly 'com.mysql:mysql-connector-j' }처럼 말이죠! build.gradle 을 올려주시면 저도 한 번 더 확인해보겠습니다.감사합니다! 🙇
질문&답변
컬럼명
안녕하세요 wk님!! 🙂 AI 인턴이 잘 대답해 주었네요!기본적으로 camelCase 형식이 snake_case로 매핑되기에 warehousing_date로 매핑하려 하는게 맞습니다.@Column(name = "warehousingDate")로 해도 안 되고 db의 컬럼명을 warehousing_date로 수정해야 정상 작동하는데라고 해주신 부분은... 아마 Column을 명시적으로 적어주면 camelCase 일지라도 매핑이 잘 될텐데.... 정확한 에러 로그 올려주시면 제가 한 번 확인해보겠습니다.다만 아직 개발 진행 중인 기능이라면, 개인적으로는 DB column을 snake_case로 바꾸시는 것을 추천드립니다. 차후에 운영 대응을 할 때에나 유지보수를 할 때에 전체 컨벤션이 맞으면 아무래도 편한 측면이 있어서요 🙂 답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇