실습 자료 부분 업로드 다시 부탁드립니다!
미해결
코틀린 함수형 프로그래밍 - 입문편
지금 올려주신 실습 자료가 문제가 있는 것 같습니다! 압축을 해제하면 .idea 디렉터리만 존재하고 내부 코드는 없는데 이 확인 부탁드려요!
- kotlin
- 함수형-프로그래밍
- 객체지향
- functional
173만명의 커뮤니티!! 함께 토론해봐요.
미해결
코틀린 함수형 프로그래밍 - 입문편
지금 올려주신 실습 자료가 문제가 있는 것 같습니다! 압축을 해제하면 .idea 디렉터리만 존재하고 내부 코드는 없는데 이 확인 부탁드려요!
해결됨
jpa, querydsl 을 완전 걷어내고 jooq 만 사용해보려 하는중인데, 아무래도 모든테이블에 "작성일, 작성자, 수정일, 수정자" 정보가 들어갈거 같아 auditing 기능을 구현 하려 하는중입니다. 프로페셔널 상품을 사용하기는 어려운 상황이고, RecordListener 를 이용하여 구현해보려 하는데 리스너가 제대로 등록이 되질 않고 있네요 ㅠㅠ @Repository class UserRepository( private var dslContext: DSLContext, ) { init { val config = dslContext.configuration().derive() config.set(userRecordAuditListener()) dslContext = DSL.using(config) } private fun userRecordAuditListener(): RecordListener { val createAudit: (UsersRecord) -> Unit = { it.createdAt = LocalDateTime.now() it.createdBy = 1 } val updateAudit: (UsersRecord) -> Unit = { it.updatedAt = LocalDateTime.now() it.updatedBy = 2 } return RecordAuditListenerGenerator<UsersRecord>().generate( UsersRecord::class.java, createAudit, updateAudit, ) } fun insertUser(user: UsersRecord): Long? { return dslContext .insertInto( USERS, USERS.NAME, USERS.AGE, ).values( user.name, user.age ).returningResult(USERS.ID) .fetchOneInto(Long::class.java) } } class RecordAuditListenerGenerator<R : UpdatableRecordImpl<R>>( ) { fun generate( recordClass: Class<R>, insertStart: (R) -> Unit, updateStart: (R) -> Unit, ): RecordListener { return object : RecordListener { override fun insertStart(ctx: RecordContext) { println("============== 1 ==============") insertStart(recordClass.cast(ctx.record()) as R) } override fun updateStart(ctx: RecordContext) { println("============= 2 ===============") updateStart(recordClass.cast(ctx.record()) as R) } } } } 데이터는 insert 되는데 작성자, 작성일이 안들어가고 있네요. 찍어놓은 프린트도 호출이 안되구요..혹시 왜그런지 알 수 있을까요..?
해결됨
코틀린 코루틴 완전 정복
- 학습 관련 질문을 남겨주세요. 질문을 상세히 작성하면 더 좋습니다. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 먼저 좋은 강의 감사합니다! 혼자서 다음과 같이 마구마구 찍어보고있었습니다. 이때 저희 예상과 다르게 돌아가서 질문을 드립니다. 저의 가설로는 "withContext는 코루틴을 생성하지 않고 Context만 바꾸니 코루틴을 제어하고 추적하는데 사용되는 Job은 새로 만들어지지 않을 것이다. 따라서 job1과 job5는 같을 것이다." 이었습니다. 하지만 결과는 새로운 job을 만들고 job의 부모에 job1을 연결시키더군요. 동일한 코루틴에서 동작하는데 왜 새로운 Job을 만드는 것인가요? fun main() = runBlocking { val job1 = coroutineContext[Job] // 1 coroutineScope { val job2 = coroutineContext[Job.Key] val job3 = launch {} val job4 = launch {} println("job2 = ${job2}") // 2 println("job2 parent = ${job2?.parent}") println("job3 parent = ${job3.parent}") println("job4 parent = ${job4.parent}") } withContext(Dispatchers.Default) { val job5 = coroutineContext[Job.Key] println("job5 = ${job5}") println("job5 parent = ${job5?.parent}") } println("job1 = ${job1}") } // // job5 = DispatchedCoroutine{Active}@4311e223 // job5 parent = BlockingCoroutine{Active}@1c2c22f3 // job1 = BlockingCoroutine{Active}@1c2c22f3 coroutineScope도 주석 1, 2 부분이 같은 코루틴이라 job1이랑 job2가 동일하게 나올 거라 생각했는데 다르군요. 그렇다면 현재 상황에서 "하나의 코루틴에 여러 개의 job(?)을 가진 것 아닌가? 이러면 job으로 코루틴을 제어할 수 있나?" 라는 의문이 듭니다. job과 코루틴의 관계가 헷갈립니다 😱
해결됨
코틀린 고급편
먼저 강의 정말 감사합니다.. 질문 폭탄을 드려서 매번 죄송할따름입니다.. 1번 질문 // inline 프로퍼티 class InLinePropertyPerson(val name: String) { inline val uppercaseName: String get() = this.name.uppercase() // 가능 // get() = field.uppercase() // 불가능 } 위의 코드처럼 inline 프로퍼티의 경우 this로의 접근은 가능한 반면, backing field 사용시에는 불가능하더라구요! 이렇게 되는 이유or원리가 궁금합니다 2번 질문 inline 사용의 경우 디버깅이 어렵다는 말을 어느 컨퍼런스의 코틀린 주제 네트워킹 세션때 들은 것 같습니다 그런 경우 강의에서 나온 대로 invoke를 유발할 수 있는 함수 파라미터의 경우 noinline을 붙이면 해결되는 부분일지, 아니면 inline을 제외한 일반 함수로 만들어서 디버깅으로 문제점을 찾고 다시 inline fun으로 만드는게 나은지 궁금합니다 (어떤 경우 차라리 메서드 콜스택을 만드는 편이 진입점을 파악하기 좋으니깐요!) 3번 질문 inline 키워드와는 관계 없는 질문이긴하지만..! // 1번 listOf(1, 2, 3) .map { num -> val result = num + 1 result } // 2번 listOf(1, 2, 3) .map { num -> val result = num + 1 return@map result } 위와 같은 코드에서 보면 map의 중간연산에 최종적으로 result가 반환될거야 라고 명시적으로 나타내는 2번 방식과 kotlin의 특징인 scope { } 의 최종 line 값을 반환하는 자동기능이 쓰인 1번 방식이 있는데 코틀린을 처음 접하거나 모르는 사람이 보면 저기 왜 뜬금없이 쓰이지도 않는 변수인 result가 있지? 이상하군! 이렇게 생각할수도있을것같아요(그냥 예시 중 하나.. 아마 이렇게 생각하시는 분은 없다고 예상) 오히려 2번 방식에서 아 여기 map에서의 최종연산 결과는 result구나 하고 map라벨링을 달고 리턴하는게 좀 더 가독성이 있어보이는데요! 2번 방식은 라벨링을 해서 jump를 하는 방식이라 안티패턴으로 봐야하는지? 아니면 그렇지 않은지?가 궁금합니다 강의 잘 보고있습니다! 감사합니다 :)
해결됨
코틀린 고급편
jmh { threads = 1 fork = 1 warmupIterations = 1 iterations = 1 } JVM/JDK toolchain : 21 kotlin { jvmToolchain(21) } .gradle 버전 : 8.8 kotlin 버전 : 2.0(컴파일러, 언어, API) 위 세 버전 기준으로는 id("me.champeau.jmh") version "0.7.2" 버전으로 하고 $ ./gradlew jmh 실행이 에러없이 잘 작동하고 실행결과는 build/reports/jmh/results.txt의 경로가 아닌 build/results/jmh/results.txt 경로에 파일이 생긴 것을 확인했습니다
해결됨
코틀린 고급편
강사님 Generic Func에서 // origin fun <T> List<T>.hasIntersection(other: List<T>): Boolean { return (this.toSet() intersect other.toSet()).isNotEmpty() } // error fun <T> List<T: Any>.hasIntersection(other: List<T>): Boolean { return (this.toSet() intersect other.toSet()).isNotEmpty() } // error fun <T> List<T>.hasIntersection(other: List<T : Any>): Boolean { return (this.toSet() intersect other.toSet()).isNotEmpty() } // error fun <T> List<T: Any>.hasIntersection(other: List<T : Any>): Boolean { return (this.toSet() intersect other.toSet()).isNotEmpty() } // ok fun <T : Any> List<T>.hasIntersection(other: List<T>): Boolean { return (this.toSet() intersect other.toSet()).isNotEmpty() } 이렇게 되더라구요! 맨 앞에 <T>가 반환타입(List<T>)과 인자타입(other: List<T>) 까지 함께 선언해주는 declaration-site variance의 형태를 띄고 있어서 그런건가요? 생각해보면 들어갈 수 있는 타입은 nullable인데 반환타입은 not null이거나 그 반대가 되면 헷갈릴 것 같아요. 그것을 막아둔것같기도 하구요! 이렇게 제네릭 메서드를 만드는 경우에도 generic 제약을 줄 수 있는거죠!? 좀 더 실 사용에 가깝게 타입가드까지 하려면 fun <T : Number> List<T>.hasIntersection2(other: List<T>): Boolean { return (this.toSet() intersect other.toSet()).isNotEmpty() } 이런 느낌으로 쓰일것같은데.. 맞나요? 코틀린 고급편 잘 듣고있습니다 ~_~
미해결
자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)
클래스 속성에 val로 정의할 경우 속성 값을 외부로 반환할 때 getter 메소드를 만들어서 반환하시나요? 아니면 속성 그대로 인스턴스.속성 으로 바로 반환하시나요?? 만약에 후자일 경우 캡슐화 위반이 아닌지 생각이 듭니다...
해결됨
자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)
안녕하세요! 최태현 지식공유자님 코틀린 강의 만들어주셔서 감사합니다! 💚 실제로 코프링 개발자로 1년정도 근무했던 경험이 있었는데.. 일했던 날짜와 한참 늦은 2024년 말이지만 추후에 강의를 완강하게 됐는데 매우 유익했습니다 몇가지 질문이 있어서 질문 남깁니다!! 질문들 TabWidth에 대해서 IntelliJ에 보시면 tabWidth를 2로 설정하셨는데, 특별한 이유가 있으신가요? 보통 저는 4로 설정하는 편이어서요! 순수함수에 대해서 제가 알기로는 내부의 scope에서 동작할때 외부의 요인이 내부의 값을 바꾸지 못하거나 내부의 요인으로 외부의 값이 바뀌지 못하계 경계가 있어서 side effect가 없는 함수를 뜻하는 걸로 알고있는데요 Java는 그렇기에 Stream api를 사용할때 final이 아닌 외부의 값을 참조하지 못하기때문에 좀 더 안전하게 작성할 수 있는데, 코틀린은 Closure, Currying 등을 허용하기 때문에 개발자의 실수를 더 많이 일으킬 가능성을 열어둔 것은 아닌지 궁금합니다 OOP와 FP에 대해서 자바같은 경우는 함수형 인터페이스또는 인터페이스 구현체를 익명클래스나 익명함수 등으로 넘겨서 FP를 모방하는걸로 알고있는데요, 대신 최소한의 getter/setter를 추가하고 행위를 나타내는 메서드명을 외부로 노출하고(public..), 그 메서드는 내부호출(private)로 캡슐화를 할 수 있는데, 코틀린처럼 invoke가 될 수 있는 함수를 인자로 넘기면 아래와 특정한 행위를 나타내는 이름이 아닌 행위 자체를 넘기게 돼서 객체의 역할과 책임을 나타내는 메서드가 없게 되서 rich domain model이 아닌 anemic domain model이 되는게 아닌지 궁금합니다 // OOP fun doHobby(person: Person?) { println("${person?.hobby}생활을 즐기고 있습니다") } fun beAging(person: Person?) { println("내년에 ${person?.age?.plus(1)}만큼 늙어갈 예정입니다") } fun mainOOP() { val person = Person("보키", 20) doHobby(person) beAging(person) } // FP fun doSomething(person: Person?, run: () -> Unit) { } fun mainFP() { Person("보키", 20).run { doSomething(this) { println("${hobby}생활을 즐기고 있습니다") } } Person("보키", 20).run { doSomething(this) { println("내년에 ${age+1}만큼 늙어갈 예정입니다") } } Person("보키", 20).run { doSomething(this) { println("런닝중입니다 건들지마시오") } } } data class에 대해서 대부분의 기능들이 강의에 녹아있기는 하지만 data class의 강력한 기능 중 하나인 copy 메서드는 백엔드의 프로덕션 개발과 테스트 코드에서 어디 부분에 사용될 수 있을까요? 이건 많이 강의 주제를 넘어간 질문이긴 하지만.... Kotlin+Springboot+JPA 를 사용할때 data class/class 중 어떤 클래스로 Entity를 만드시는지 궁금합니다. MySQL or MongoDB or PostgreSQL에서 어떤 클래스와 궁합이 좋은지. data class를 사용한다면 copy를 어느 부분에 사용하면 좋을지도 알고싶습니다! 강의내용과는 많이 무관하기에(코틀린이 아닌 서버, 타 라이브러리, 인프라(DB)까지 논의확장을 하였음) 답하기 어렵다면 안해주셔도 괜찮습니다 Scope Func에 대해서 저는 Kafka, Configuration 등에서 기본객체를 가져와서 apply를 이용해서 초기값 -> 덮어씌우는 방식을 주로 사용했고 with구문을 이용해서 log를 편하게 했었는데 scope func의 또 다른 좋은 선례도 알고싶습니다! Ex1) private fun <K : Any, V : Any> initConsumerProps( keyDeSerClass: Class<out Deserializer<K>>, valueDeSerClass: Class<out Deserializer<V>> ): Properties = Properties().apply { put(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092") put(KEY_DESERIALIZER_CLASS_CONFIG, keyDeSerClass.name) put(VALUE_DESERIALIZER_CLASS_CONFIG, valueDeSerClass.name) put(GROUP_ID_CONFIG, "group-02") put(MAX_POLL_INTERVAL_MS_CONFIG, "60000") } Ex2) for (record in consumerRecords) { with(record) { logger.info { "key: ${key()}, partition: ${partition()}, offset: ${offset()}, value: ${value()}" } } } method reference에 대해서 어떤 코드에서는 let(::XX), 또 다른 코드에서는 let { ... }방식이 사용될 때가 있는데, 어떤게 성능상 또는 가독성에서 좋은지도 궁금합니다!
해결됨
자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 강사님 9:54쯤에 질문이 있습니다 val 또는 fun으로 확장함수를 만들 수 있는데.. 두가지 방식의 차이점이 있나요? 또는 이펙티브 코틀린 등등에서 더 선호하는 방식이 있는지, 안티패턴이나 best practice가 있다면 알고 싶습니다. 차이점이 있다면 확장함수에 val을 사용한 경우 custom getter방식으로 구현되고 호출부에 ()가 없는 반면, fun키워드를 사용한 경우에는 get()이 사용되지 않고 호출부에 ()를 해줘야 되는 것 같습니다 1번방식: 강의에서 나온대로 val _ 정의부: val List<Fruit>.samePriceFilter: List<Fruit> get() = this.filter(Fruit::isSamePrice) _ 호출부: val samePriceFruits2 = fruitsInList.flatMap { it.samePriceFilter } 2번방식: fun으로 사용 _ 정의부: fun List<Fruit>.samePriceFilter(): List<Fruit> { return this.filter(Fruit::isSamePrice) } _ 호출부: val samePriceFruits2 = fruitsInList.flatMap { it.samePriceFilter() }
미해결
입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
안녕하세요. 크롬에서 프로그램을 열 수 없습니다. ( 11:00 ) https://github.com/bohuiKang/portfolio_bohui 아래에 에러 메시지입니다. 어떤 부분이 문제인지 찾지 못했습니다.. 어디가 문제인지 알 수 있을까요? 2024-12-12 18:14:04 2024-12-12T09:14:04.434Z INFO 1 --- [nio-8080-exec-8] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2024-12-12 18:14:04 2024-12-12T09:14:04.434Z WARN 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 08S01 2024-12-12 18:14:04 2024-12-12T09:14:04.434Z ERROR 1 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Communications link failure 2024-12-12 18:14:04 2024-12-12 18:14:04 The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. 2024-12-12 18:14:04 2024-12-12T09:14:04.436Z WARN 1 --- [ main] o.h.e.j.e.i.JdbcEnvironmentInitiator : HHH000342: Could not obtain connection to query metadata 2024-12-12 18:14:04 2024-12-12 18:14:04 org.hibernate.exception.JDBCConnectionException: unable to obtain isolated JDBC connection [Communications link failure ... 2024-12-12 18:14:04 2024-12-12T09:14:04.574Z WARN 1 --- [ main] org.hibernate.orm.deprecation : HHH90000025: MySQLDialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default) ... 2024-12-12 18:14:21 2024-12-12T09:14:21.680Z WARN 1 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 08S01 2024-12-12 18:14:21 2024-12-12T09:14:21.680Z ERROR 1 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Communications link failure 2024-12-12 18:14:21 2024-12-12 18:14:21 The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. 2024-12-12 18:14:21 2024-12-12T09:14:21.742Z INFO 1 --- [nio-8080-exec-1] c.b.p.a.advice.AdminApiControllerAdvice : Could not open JPA EntityManager for transaction 2024-12-12 18:14:21 2024-12-12 18:14:21 org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction ...
해결됨
코틀린 코루틴 완전 정복
- 학습 관련 질문을 남겨주세요. 질문을 상세히 작성하면 더 좋습니다. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 안녕하세요, coroutine 강의를 듣고 ktor server 공부를 하는 중에 delay 관련 질의가 생겨 글 남깁니다. delay 의 경우 coroutine 이 스레드를 양보하고, 일정 시간 후에 다시 스레드가 비어있으면 점유하는 형식으로 진행될텐데, 아래와 같은 경우 delay 이후 코드가 실행되지 않습니다. 사용자 → KTOR Server → suspend function call → loop { logic → 너무 많은 동작을 제한하기위한 delay } ktor 는 기본적으로 요청이 IO Dispatcher 를 사용하는 것 같은데, coroutine 에서 delay 이후 기능이 동작하지 않는 것에 대한 이유가 있을까요..? (강의랑 무관한 내용이라 죄송합니다 ㅠ.ㅠ) /* Routing.kt */ fun Application.configureRouting(searcher: BaseSearcher) { routing { post("/search") { val params = call.receive<Map<String, String>>() searcher.logging("Received POST request with params: $params") val result: List<BaseDTO> = searcher.search(params) searcher.logging("POST Result: $result") call.respond(HttpStatusCode.OK, mapOf("result" to result)) } get("/") { call.respondText("Hello World!") } } } /* Searcher.kt */ /** * 함수 이름 : search * 내용 설명 : 입력된 map<string, string> param 을 바탕으로 요청 전송 및 응답 반환 */ override suspend fun search(searchParam: Map<String, String>): List<SimpleSearchLandResultDTO> { // 별도의 Job 으로 분리해서 오류시 상위 전파 제한 return HttpClient(CIO).use { client -> val parseResultLst: MutableList<SimpleSearchLandResultDTO> = mutableListOf() try { val initRequestResultDto: SimpleSearchResultDTO = sendFormedRequest(client, SearcherConst.URL_MAIN_PAGE, false) // 기본 페이지 요청이 성공했을 때 다음 요청을 진행 if (initRequestResultDto.isSuccess) { val mutableSearchParam = searchParam.toMutableMap() delay(300) while (true) { val eachPageSearchResultDto: SimpleSearchResultDTO = sendFormedRequest(client, makeGetUrl(mutableSearchParam), true) // 실패한 경우 종료 if (!eachPageSearchResultDto.isSuccess) { logging("[search] 조회 중 오류가 발생하였습니다.") break } // 결과가 빈 경우도 종료 else if (eachPageSearchResultDto.landSearchResultLst.isEmpty()) { break } // 결과가 있는 경우엔 전부 더해줌 parseResultLst.addAll(eachPageSearchResultDto.landSearchResultLst) logging("[search] result(${eachPageSearchResultDto.landSearchResultLst[0].currentPage} : ${eachPageSearchResultDto.landSearchResultLst}") // 마지막 페이지인 경우 종료 if (eachPageSearchResultDto.landSearchResultLst[0].currentPage == eachPageSearchResultDto.landSearchResultLst[0].maxPage ) { break } else { // 다음 페이지 수집 진행 mutableSearchParam["currentPage"] = (eachPageSearchResultDto.landSearchResultLst[0].currentPage + 1).toString() delay(300) } } } } catch (e: Exception) { logging("[search] search 함수 중 오류가 발생하였습니다. param: $searchParam, exception: $e") } logging("[search] Result : $parseResultLst") // parseResultLst 반환 parseResultLst } } private suspend fun sendFormedRequest(client: HttpClient, url: String, isResultNeed: Boolean): SimpleSearchResultDTO { return try { val response = client.get(url) { headers { append(HttpHeaders.Cookie, cookie.toCookieString()) append(HttpHeaders.Accept, SearcherConst.DEFAULT_HTTP_ACCEPT) append(HttpHeaders.AcceptEncoding, SearcherConst.DEFAULT_HTTP_ACCEPT_ENCODING) append(HttpHeaders.Connection, SearcherConst.DEFAULT_HTTP_CONNECTION) append(HttpHeaders.AcceptLanguage, SearcherConst.DEFAULT_HTTP_ACCEPT_LANGUAGE) append(HttpHeaders.UserAgent, SearcherConst.DEFAULT_HTTP_USER_AGENT) } } cookie.putAll(response.headers[HttpHeaders.SetCookie]?.split(";")?.map { it.split("=") }?. filter { it.size == 2 }?.filter { it[1] != "/" && it[1].isNotEmpty() }?.associate { it[0] to it[1] } ?: mapOf()) if (response.status != HttpStatusCode.OK) { SimpleSearchResultDTO(false) } else { SimpleSearchResultDTO(true, if(isResultNeed) parseData(response.bodyAsText()) else mutableListOf()) } } catch(e: Exception) { logging("[sendFormedRequest] Error : $e", Level.ERROR) SimpleSearchResultDTO(false) } }
미해결
[LV1] Jetpack Compose - UI 연습하기
안녕하세요 TextField 커스텀 관련하여 여러 기능을 살펴보고 있는데요. 구글에서 찾아봐도 Cursor의 두께를 설정하는 프로퍼티를 찾지 못하여서 질문드립니다. Cursor를 커스텀하는 방법이 있나요? 두께를 줄이거나 높이를 낮추는 등의 커스텀을 진행하고 싶습니다. 감사합니다.
해결됨
코틀린 코루틴 완전 정복
안녕하세요. 강사님 코루틴 강의를 1번 완강하고, 최근에는 전체적으로 강의를 복습을 하고 있습니다. 복습을 하다가 CoroutineDispatcher 의 limitedParallelism 에 대해 몇가지 궁금한 점이 생겨서 질문을 남기게 됐습니다. Dispatchers.Default 관련 Dispatchers.Default 의 스레드풀 수를 넘어서는 숫자 혹은 스레드풀 수에 딱 맞게 스레드 수를 지정하여 limitedParallelism 함수를 실행하는 경우, Dispatchers.Default 의 모든 스레드들은 제가 지정한 해당 작업을 처리하기 위해 계속 작업을 하고, 해당 작업이 시작된 이후에 Dispatchers.Default 로 지정하여 실행한 코루틴 작업들은 제가 지정한 작업이 끝나기 전까지는 모두 대기하게 되는 것인가요?? 예를 들어, CPU 코어가 4개인 컴퓨터라면 Dispatchers.Default 의 스레드 풀에 들어있는 스레드 수는 4개가 될테고, 해당 상황에서 Dispatchers.Default.limitedParallelism(4) 혹은 스레드가 몇개 있는지 몰라 Dispatchers.Default.limitedParallelism(8) 이렇게 지정하는 경우에 아래 코드를 기준으로 하면 제가 먼저 시킨 작업( superCpuIntensiveTask() )이 끝나기 전에는 lightCpuIntensiveTask() 는 실행되지 않는 것일까요? (※ 멀티 스레드 작업이기 때문에 아래의 lightCpuIntensiveTask() 가 먼저 실행될 가능성도 있지만 아주 재수가 나쁘게 superCpuIntensiveTask() 가 먼저 실행이 된다면 어떻게 되는지가 궁금합니다) // CPU 코어가 4개인 컴퓨터 fun main() = runBlocking { launch(Dispatchers.Default.limitedParallelism(8)) { superCpuIntensiveTask() } launch(Dispatchers.Default) { lightCpuIntensiveTask() } println("Done") } 1번과 비슷한 질문입니다. CPU 코어가 4개인 컴퓨터에서 Dispatchers.Default.limitedParallelism 를 여러번 사용한 경우, 코드상으로 보면 사용해야 하는 스레드의 수가 전체 스레드풀 수를 넘어서는 숫자가 되는데 이 경우에는 어떻게 스레드를 나눠서 사용하는 걸까요 ? // CPU 코어가 4개인 컴퓨터 fun main() = runBlocking { launch(Dispatchers.Default.limitedParallelism(3)) { superCpuIntensiveTask() } launch(Dispatchers.Default.limitedParallelism(3)) { superCpuIntensiveTask2() } launch(Dispatchers.Default.limitedParallelism(2)) { CpuIntensiveTask() } println("Done") } Dispatchers.IO 관련 Dispatchers.IO.limitedParallelism 를 사용하면 새로운 Thread 집합을 만든다고 말씀해주셨습니다. 그림에서 표현해주신 것처럼 기존에는 개별로 존재하는 Thread들을 새로 그룹으로 묶어낸다고 이해를 하면 되는 것일까요 ?? Dispatchers.IO.limitedParallelism 으로 묶여있던 Thread들은 해당 코루틴이 끝나면 다시 그룹이 풀리는 것일까요 ?? 만약에 Dispatchers.IO.limitedParallelism 를 많이 사용하여 이미 기존의 스레드들 모두가 집합으로 구성이 되어있는 경우에는 신규로 집합을 만들 수가 없는 경우에는 해당 Dispatchers.IO.limitedParallelism 요청이 어떻게 되는지 궁금합니다. limitedParallelism 의 Thread 수 지정 해당 코드와 같이 Dispatchers.Default.limitedParallelism(4) Thread 수를 지정해야 할때, 어떤 기준으로 어떻게 Thread 수를 지정해야 좋을까요 ?? 강사님만의 팁이 있으시면 공유가 가능하실까요?? 어떻게 수를 정해서 넣어야 할지 감이 잘 잡히지 않습니다. 😢 1번과 관련된 질문입니다. Thread 수를 지정할 때 현재 Thread Pool에 존재하거나 남아있는 쓰레드가 몇개인지 충분히 고려를 하면서 코드를 짜야할까요 ?? 실수로 스레드 풀의 전체 Thread 수를 넘어선 요청을 하게 되면 어떻게 되는지 궁금합니다. 질문이 많아서 정말 죄송합니다. 🙇♂ 🙇♂ 🙇♂
미해결
[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린(Android Kotlin)
저는 다이어트는 내일부터 하는거 아닙니까 창 뜬이후로 창이 꺼져버리는데 이유가 멀까요?
미해결
[초급편] 안드로이드 커뮤니티 앱 만들기(Android Kotlin)
안녕하세요, 강의 잘 듣고 있습니다. 섹션 7의 게시글 이미지가 파이어베이스에 저장되지 않아서 글을 남깁니다. 사진을 넣어서 업로드하면 Storage에 업로드 되지 않습니다. 기존 코드로 이미지가 업로드가 되지 않아서 구글링과 chatGpt의 도움을 받는 과정에서 storage에 저장하는 경로 설정을 수정해서 폴더를 따로 만들었습니다. 다른 질문글을 보고 보안 규칙을 수정해봐도 올라가지 않습니다. 사용량을 살펴보니 요청은 1건 있지만 허용량이 없습니다. 여러 방법을 시도해봐도 되지 않아서 질문글을 남깁니다. 전체 코드 공유를 위해 깃허브 주소를 남깁니다! https://github.com/Joodo7ng/my-sole-life.git
해결됨
[초급편] 안드로이드 커뮤니티 앱 만들기(Android Kotlin)
안녕하세요, 섹션 7의 게시글 이미지 업로드 강의를 듣다가 생긴 오류를 오랫동안 해결하지 못해서 질문글을 남깁니다. BoardWrite의 imageArea를 클릭시 갤러리에 사진이 없어서 가져올 수 없다고 뜹니다. 이미지 파일을 크롬에서 다운받을때 경로를 sdcard로 설정해서 다운받아도 Device explorer를 통해 살펴보면 저장된 파일이 없습니다... 터미널에서 강제로 경로를 변경시켜보았으나 갤러리에 표시되진 않았습니다. 어떻게 해결해야할까요?
해결됨
코틀린 코루틴 완전 정복
- 학습 관련 질문을 남겨주세요. 질문을 상세히 작성하면 더 좋습니다. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 아무리 생각해도 코루틴 안에서 사용하는 코루틴 안의 delay나 await 등 점유 해제 가능한 fun들을 코드 중복을 피하기 위해 따로 빼놓은 delay나 join await를 포함한 코루틴용 fun의 키워드 같은데 구글링해서 보면 마치 delay나 await, join 이 포함된 fun 은 무조건 suspend를 사용하고 점유 해제 되도록 한다고 설명한데가 많네요. 결론은 suspend 자체가 점유 해제 하고 resume을 시켜주는 키워드가 아닌 것 같은데 제가 이해한게 맞나요?
해결됨
코틀린 코루틴 완전 정복
- 학습 관련 질문을 남겨주세요. 질문을 상세히 작성하면 더 좋습니다. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. section10의 code4/Code10-4에서 보면 코드가 아래와 같이 되는데 package section10.code4 import kotlinx.coroutines.* fun main() = runBlocking<Unit> { val startTime = System.currentTimeMillis() repeat(10) { repeatTime -> launch { Thread.sleep(1000L) // 1초 동안 스레드 블로킹(코루틴의 스레드 점유 유지) println("[${Thread.currentThread().name}] 작업 실행 ") println("[${getElapsedTime(startTime)}] 코루틴${repeatTime} 실행 완료") } } } fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 보통 이런 코드는 이렇게 멀티스레드로 처리하지 않나요..? import java.util.concurrent.Callable import java.util.concurrent.Executors fun main() { val startTime = System.currentTimeMillis() val es = Executors.newFixedThreadPool(10) val callTasks = mutableListOf<Callable<Int>>() repeat(10) { repeatTime -> val callTask = Callable { println("[${Thread.currentThread().name}] 작업 실행 ") return@Callable repeatTime } callTasks.add(callTask) } val results = es.invokeAll(callTasks) // 결과 출력 results.forEach { future -> println("Result: ${future.get()}") } println("[${getElapsedTime(startTime)}] 실행 완료") es.close() } fun getElapsedTime(startTime: Long): String = "지난 시간: ${System.currentTimeMillis() - startTime}ms" 음.. 그리고 스레드 1개를 만들어서 run을 시키면 1MB정도의 메모리 비용이 발생하고 context switch도 일어나지만, 코루틴은 훨씬 더 값싸다고 알고있습니다 추가로 아직 r2dbc처럼 비동기 트랜잭션 처리 등.. 이게 지원이 좀 미약하다고 알고있습니다. 서버의 작업은 대체로 CPU를 사용하는 부분이 그렇게 많이 없고 DB에 쓰고 값을 가져오는 동기화 코드, 순차처리 작업이 많은걸로 알고 있습니다. 그럼 언제 멀티스레드를 사용하는게 좋고, 언제 코루틴을 사용하는게 좋을까요?
미해결
[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린(Android Kotlin)
저는 안드로이드 화면을 켜고 구동시켜 볼려고 하면 화면 자체가 안켜 지네요 ~ 이유가 멀까요 ㅠㅠ 쌤 ~.
해결됨
코틀린 코루틴 완전 정복
안녕하세요. 강사님 강사님 덕분에 코루틴에 대한 이해도가 많이 높아져서, 최근 코루틴을 활용한 비동기 서버 구현에 대해서 재밌게 개발을 하고 있습니다. (코루틴이 재미는 있는데, 비동기라는 개념 때문에 너무 어려워서 머리가 아프네요 😢 ) spring webflux와 코루틴을 활용한 서버 개발을 하다보니, 자연스럽게 suspend 키워드가 많이 사용되고 일시 중단 함수에 대해서 조금 더 높은 이해도가 필요해서 이에 대해서 몇가지 질문을 하려고 합니다. 1.저는 강사님의 일시 중단 함수 관련 강의를 듣고 일시 중단 함수란 그냥 일시 중단 지점을 포함할 수 있는 특이한 함수 , suspend 함수에서 코루틴을 사용하기 위해서는 coroutineScope을 사용하자 이렇게 이해를 했어서 다음과 같이 코드를 짰습니다. class SuspendRequestTest { @Test fun suspendRequestTest() = runBlocking { val startTime = System.currentTimeMillis() // 시작 시간 기록 println("suspendRequest start") anotherSuspendFunction1() // suspend function 1 호출 anotherSuspendFunction2() // suspend function 2 호출 println("suspendRequest end") val endTime = System.currentTimeMillis() // 종료 시간 기록 println("suspendRequest elapsed time: ${endTime - startTime} ms") // 경과 시간 출력 } private suspend fun anotherSuspendFunction1() = coroutineScope { println("anotherSuspendFunction1 start") launch { println("anotherSuspendFunction1 extra logic start") delay(1000) println("anotherSuspendFunction1 extra logic end") } println("anotherSuspendFunction1 end") } private suspend fun anotherSuspendFunction2() = coroutineScope { println("anotherSuspendFunction2 start") launch { println("anotherSuspendFunction2 extra logic start") delay(1000) println("anotherSuspendFunction2 extra logic end") } println("anotherSuspendFunction2 end") } } // 실행 로그 suspendRequest start anotherSuspendFunction1 start anotherSuspendFunction1 end anotherSuspendFunction1 extra logic start anotherSuspendFunction1 extra logic end anotherSuspendFunction2 start anotherSuspendFunction2 end anotherSuspendFunction2 extra logic start anotherSuspendFunction2 extra logic end suspendRequest end suspendRequest elapsed time: 2037 ms 제 예상대로라면 1초가 살짝 넘는 시간으로 작업이 완료되어야 하는데, 해당 작업은 2초가 걸리는 작업이었습니다. (로그도 항상 1번이 먼저 나타납니다.) 1-1) 혹시 coroutineScope 은 내부의 코드가 완료되기 전까지는 다음으로 넘어가지 않는 Blocking 속성을 가지는 함수일까요 ? 1-2) supervisorScope 도 coroutineScope 처럼 내부의 코드가 완료되기 전에는 다음으로 넘어가지 않는다고 알면 될까요? 1-3) 일시 중단 함수가 여러 일시 중단 함수들을 내부 로직으로 가지는 경우, 순차적으로 각각의 일시 중단 함수가 끝나야지만 다음 일시 중단 함수가 실행되는 걸까요 ? 2.제가 실제 코루틴 사용 사례 및 코드를 많이 보지 못해서, 어떻게 쓰는게 좋은 케이스 인지 이해도가 낮은 것 같습니다. 보통 coroutineScope 혹은 supervisorScope 함수들은 어떤 경우에 어떤 목적을 위해 주로 쓰이는 걸까요 ?? coroutineScope 의 속성이나 특징에 관해서 추가적으로 제가 알아야 하거나 참고하면 좋을 자료가 있을까요?? 😊