묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
도메인 객체와 엔티티 객체 사용
안녕하세요, 강의랑 유튜브 너무 잘 보고있습니다.강의를 보면서 여러 인사이트를 얻었습니다!궁금한 점은 코틀린이 아닌 자바를 사용할 경우엔 엔티티 객체를 도메인 객체로 변환해서 return 해줄 때 코드가 다소 지저분해지는데요 ㅠㅠ어떤 방식이 가장 유효할지 의견 부탁드립니다! 엔티티 객체에 toDomain() 과 같은 도메인 객체 변환 메서드 생성Mapper 클래스 생성 (ex. ProductMapper.toDomain(entity)) (생성 한다면 어느 모듈, 위치에..?)추가적으로 도메인 객체를 사용한다면, 엔티티 -> 도메인 -> 클라이언트 응답 DTO 와 같은 변환 과정을 거의 필수적으로 거쳐가야하는데, 이 부분에 대해서 도메인 객체와 엔티티 객체의 분리 시점이 재미니님은 있으신건가요?아니면 프로젝트 시작부터 도메인과 엔티티는 구분해서 사용할 것이다! 라고 정하고 시작하시는 편이신가요? 물론 프로젝트의 규모와 도메인의 복잡도 등에 따라 유연하게 변해야 한다고 생각하지만, 해당 경험이 없다시피 하다보니 현재 프로젝트 구조에서 구현 레이어 밖으로 나갈때 도메인 객체로 변환의 이점과 트레이드 오프에 대해 어떻게 생각하시는지 궁금합니다!
-
해결됨누구보다 빠르게 배우는 코틀린 (1시간)
섹션 5 부터 사운드가 안 들려요.
안녕하세요.저만 그러진 않을 것 같은데 이상하네요.아래 영상을 시청하는데 사운드가 들리지 않습니다.섹션 5. 흐름을 제어하는 조건문9. 조건문 핵심 개념 혹시나 해서이후 영상들도 모두 시청해봤는데 모두 사운드가 들리지 않네요.
-
미해결실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
안녕하세요 혹시 프론트 코드 제공받을 수 있을까요?
안녕하세요 강의 잘 보고있습니다.다름이 아니라 프론트쪽 코드가 궁금해서 리액트 코드좀 받고싶은데 받을 수 있을까요?메일:ad0362320@naver.com 입니다. 항상 감사드립니다!!
-
해결됨멀티 모듈 아키텍처로 구현하는 은행 서버 핵심 기능 [ Kotlin & Spring ]
비동기 전송을 위한 Thread 관리 에서 Executor 설명 오류
안녕하세요! 유익한 강의 감사합니다.Executor 설명 중 한 가지를 정정드리고 싶습니다.강의에서 설명해주신 내용은 maxPoolSize까지 바로 스레드가 생성되는 것으로 이해될 수 있는데,실제 동작은 먼저 corePoolSize만큼 스레드를 생성하고,그 이후 요청은 큐에 쌓이며,큐가 가득 찼을 때 maxPoolSize까지 확장되는 구조입니다.물론 강의 흐름상 의도적으로 설명을 단순화하신 것일 수도 있습니다만,혼동될 수 있는 부분이라 참고용으로 댓글 남깁니다!
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
CouponService 의존성 의문
CouponService쪽에선 다른 Service와 다르게 Implement Layer를 의존안하고, DataAccess Layer의 Repository 계층에 의존하고 있던데, 왜 그런걸까요?
-
미해결[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린(Android Kotlin)
코딩을 완료하고난후 앱 실행시 자동 종료
스마트폰에 앱까지 설치가 다 되었습니다.근데 앱을 켜면 자동으로 실행 종료가 됩니다. 뭐가 문제 일까요? ㅠㅠ
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
상품 목록 조회 고도화 질문
상품 목록 조회가 복잡해질 때의 상황을 생각해보고 있는데요. 감이 오지 않는 부분들이 몇가지 있어 질문드립니다. 상품 목록 조회 시 옵션 개념이 있다면 재고는 어떻게 처리해야 할까요? 재고는 상품옵션들의 조합이 가지고 있는 것이 이상적일 것 같은데, 상품 목록 조회던, 상세 조회던 품절 여부와 최대 구매가능 수량을 나타내기 위해서는 재고 정보도 필요해보입니다. 따라서 Product 필드에 해당 상품의 전체재고수량(모든 조합의 실재고 합)을 필드로 가지고 있는게 맞나? 라는 생각이 들고... 필드로 가지고 있다면 상품 조회 시 조합을 전부 조회하고 매번 합해 주어야 하나? 그럼 Product 테이블 컬럼단에 미리 합한 값을 넣어두어야 하나? 여러 생각이 듭니다. 다음으로는 프로모션(타임세일) 적용가, 쿠폰적용가 등으로 보여줘야 한다면 이게 실질적으로 Product 도메인 객체의 가격을 변경해도 되는 건가? 라는 생각도 했습니다.프로모션(타임세일) 적용가, 쿠폰적용가 등으로 보여줘야 한다면 쿠폰적용가, 타임세일 적용가 등이 처음에는 보여주기만을 위한 정보이지 않을까 생각했는데, 실제 주문할 때 반영될 금액이기도 하고, 타임세일 같은 경우는 대부분 모두에게 적용되어 보이는 형태이기에... 뷰의 목적이 아니라 개념적으로 봐도 되는걸까? 라는 생각이 듭니다.조금 고도화 하여 생각해보니 감이 잡히지 않는 부분들이 너무 많네요,,, 실무에서는 주로 어떤 구조를 추구하는지 제미니님은 어떻게 생각하시는지 궁금합니다. 긴 글 읽어주셔셔 감사드려요!
-
미해결모던 안드로이드 - Jetpack Compose 입문
ViewModel 사용 관련 질문
안녕하세요, 비만도 계산기 강의를 수강하면서 코드를 작성하고 말씀하신 내용들을 학습했는데요."bmi state를 통해 bmi값이 변경되면 설정한 콜백이 동작하며 결과화면이 recomposition되고, 네비게이션 컨트롤러로 해당 화면으로 이동한다" 라고 이해했는데요,navController.navigate("result")가 수행되면composable(route = Screen.Result.route) { ResultScreen( bmi = viewModel.bmi.value, onBackClick = { navController.popBackStack() } ) }부분이 실행되기 때문에 bmi가 State가 아니고 지역변수여도 원하는 화면이 나올것이라고 생각해서 코드를 변경했는데 실제로 기존과 동일하게 잘 동작하는것을 확인하였습니다. 본 강의에서는 지역변수를 활용해서도 동작이 되지만, UI와 로직을 분리하는 장점이 있으며 ViewModel이 많이 사용되니 사용하신것으로 이해하면 될까요?아래는 지역변수로 변경한 코드입니다package com.example.obesitymachine import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import kotlin.math.pow sealed class Screen(val route: String) { data object Home : Screen("home") data object Result: Screen("result") } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { val navController = rememberNavController() var bmi = 0.0 NavHost(navController = navController, startDestination = Screen.Home.route) { composable(route = Screen.Home.route) { HomeScreen( onResultClick = {height, weight -> bmi = bmiCalculate(height, weight) navController.navigate(Screen.Result.route) } ) } composable(route = Screen.Result.route) { ResultScreen( bmi = bmi, onBackClick = { navController.popBackStack() } ) } } } } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeScreen( modifier: Modifier = Modifier, onResultClick: (height: Double, weight: Double) -> Unit ) { val (height, setHeight) = rememberSaveable { mutableStateOf("") } val (weight, setWeight) = rememberSaveable { mutableStateOf("") } Scaffold( topBar = { TopAppBar( title = { Text("비만도 측정기") } ) } ) { Box( modifier.padding(it) ) { Column( modifier .fillMaxSize() .padding(8.dp) ) { OutlinedTextField( value = height, onValueChange = setHeight, label = { Text("키") }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number ), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = weight, onValueChange = setWeight, label = { Text("몸무게") }, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number ), modifier = Modifier.fillMaxWidth() ) Button( modifier = Modifier.align(Alignment.End), onClick = { onResultClick(height.toDouble(), weight.toDouble()) } ) { Text("결과") } } } } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun ResultScreen( bmi: Double, onBackClick: () -> Unit ) { val text = when { bmi > 25 -> "과체중" bmi > 18.5 -> "정상" else -> "저체중" } val icon = when { bmi > 25 -> R.drawable.baseline_sentiment_very_dissatisfied_24 bmi > 18.5 -> R.drawable.baseline_sentiment_satisfied_24 else -> R.drawable.baseline_sentiment_dissatisfied_24 } Scaffold( topBar = { TopAppBar( title = { Text("비만도 측정기") }, navigationIcon = { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null, modifier = Modifier.clickable(onClick = onBackClick) ) } ) } ) { Box( modifier = Modifier.padding(it) ) { Column( modifier = Modifier .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text, fontSize = 30.sp) Spacer(modifier = Modifier.height(50.dp)) Image( painter = painterResource(id = icon), contentDescription = null, modifier = Modifier.size(100.dp), colorFilter = ColorFilter.tint( color = Color.Black ) ) } } } } fun bmiCalculate( height: Double, weight: Double ): Double { return weight / (height / 100).pow(2.0) }감사합니다!
-
미해결실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
실행이 안되네요
Execution failed for task ':compileKotlin'.> Error while evaluating property 'filteredArgumentsMap' of task ':compileKotlin' > Could not resolve all files for configuration ':compileClasspath'. > Could not find org.jetbrains.kotlin:stdlib-jdk8:1.6.21. Required by: project :Possible solution: - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html 위와 같은 에러가 뜨는데아래 잘 바꿨거든요? plugins { id 'org.springframework.boot' version '2.7.6' id 'io.spring.dependency-management' version '1.0.12.RELEASE' id 'java' id 'org.jetbrains.kotlin.jvm' version '1.6.21' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.jetbrains.kotlin:stdlib-jdk8' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() } compileKotlin{ kotlinOptions{ jvmTarget="11" } } compileTestKotlin{ kotlinOptions{ jvmTarget="11" } }왜안될까요
-
미해결실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기 (Java 프로젝트 리팩토링)
프론트 영역 보는법
안녕하세요 이거 프론트 부분은 다 해서 주시는데프론트 영역은 어떻게 보는지 알 수 있을까요?
-
미해결[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린(Android Kotlin)
datavinding에서 오류가 납니다.
사진처럼 binding = 할때 빨간색이 사라지지 않습니다. 에러가 나는거 같은데 뭐가 문제가 될까요?2번 사진에서 혹시 뭐 클릭해야하는게 있을까요?
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
표현 계층에서의 접근 지점이 다양해지는것과 이를 해결하기 위한 파사드의 도입에 대해 제미니님의 생각이 궁금합니다.
안녕하세요 제미니님, 유튜브부터 계속 꾸준히 보다가 강의 릴리즈 하신 후 바로 구매하여 듣고 있는 사람입니다. 먼저, 생각할 거리를 많이 주는 좋은 강의 감사드립니다. 다름이 아니라, 일단은 ProductSerivce 안에서 Product라는 개념만을 명확히 다루고자 하였기에 Controller에 타 개념의 Service들도 혼재되는? 그런 상황이라고 이해했습니다. 물론 다 선택의 영역이겠지만, 개발자분들 중에선 Presentation 영역(Controller)을 최대한 가볍게 가지고 가시려는 분들도 많은것 같습니다.그래서 대신에 여러 서비스들이 혼재되는 상황을 Facade 등을 도입해서 여러 개념의 서비스들에 대한 presentation 계층에서의 접근을 일원화하고 각 서비스단에서의 복잡성은 해결하고자 하는 케이스도 실무에서 종종 접했는데요. 이에 대해서는 제미니님께서 어떻게 생각하시는지 궁금합니다.
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
제품상세 코드 느끼기
안녕하세요 제미니님 유투브때부터 잘 보고 있습니다! 그 25분쯤에 controller에 여러 service들을 주입받아서 사용하시는 부분에 대한 질문이 있습니다.1. 그럼 격벽으로 넘어도 되는 개념 사이에서는 a service에서 b finder or b appender 를 호출해도 괜찮은건지..?2. 격벽으로 넘어서 안돼는 개념 사이에서는 강의에 나온것처럼 여러 service를 controller에서 받아서 response를 만들어야 하는건지.....?또 그렇게 되면 controller에서 여러 service를 알아야하니까 그건 또 문제가 없는지.. 궁금합니다!!제미니님은 주로 어떻게 하시나요!?감사합니다
-
해결됨[중급편] 친절한 JETPACK 개론 <상> (Android Kotlin)
dataBinding 시 오류
안녕하세요?dataBinding 강의를 듣고 실습 중 지금의 대목에서 계속 오류가 납니다.안드로이드 버전 문제인지 어떤지 잘 모르겠습니다.일단 구글 드라이브에 해당 파일을 올려 보았습니다.https://drive.google.com/file/d/1pVyzoFYj5apFNWuMUjtBqGEtG_p_wEaP/view?usp=sharing살펴보시고, 말씀 주시면 감사하겠습니다. 해당 오류 메시지의 첫 부분은 다음과 같습니다: Execution failed for task ':app:compileDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction > Internal compiler error. See log for more details Try: > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. Exception is: org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:compileDebugKotlin'. at
-
미해결[왕초보편] 앱 8개를 만들면서 배우는 안드로이드 코틀린(Android Kotlin)
안드로이드 스튜디오 버전 차이로 초기 empyt activity 선택하면 안됩니다.
안녕하세요.안드로이드 스튜디오 버전(Android Studio Otter 2 Feature Drop | 2025.2.2)에서는empty activity를 선택하면 layout 없어서 activity_main.xml 즉 UI를 설정 파일 없습니다.empty views activity를 선택해야 합니다.동영상을 수정하셔야 수강생들이 혼동이 없습니다.
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
격벽의 순환 참조(?)
안녕하세요. 지난번에도 질문 남겼었는데 또 찾아뵙게 되었습니다. user 의 경우 많은 개념들이 참조하게 될 것 같습니다.회원탈퇴라는 기능이 제공될 때, 회원이 탈퇴되면 관련된 개념들을 삭제해야된다고 할 경우 이를 어떻게 해결하는 것이 좋을까요?user 쪽에서 관련 개념들을 찾아서 삭제하기에는 개념 격벽간의 순환 참조(?)가 발생하게 될 것 같습니다.카프카나 메시지 큐 등을 이용해서 처리할 수 있을 것 같은데 현재 이를 처리할 수 있는 별도의 인프라는 없다고 가정해보고 싶습니다.그렇다면 어플리케이션 이벤트(ApplicationEventPublisher)를 사용하는 것이 방법이 생각납니다.하지만 이벤트를 사용하게 되면 어플리케이션의 로직 흐름을 보기가 조금 어려우지는 것 같다는 생각도 들어서 괜찮은 방법인지 고민이 됩니다.
-
미해결입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
Windows 환경에서 작업중
안녕하세요, 윈도우 환경에서 작업중입니다.다름이 아니라 Gradle을 IDEA로 변경하면 Build 시에 Error가 떠서 AI에게 물어보니 Gradle로 변경하라 하여 변경하니 되는데 이렇게 작업해도 상관 없는 부분일까요? 오류메세지:Kotlin: [Internal Error] java.lang.NoClassDefFoundError: org/jetbrains/kotlin/com/intellij/psi/PsiElement at org.jetbrains.kotlin.noarg.fir.KtErrorsNoArg.<clinit>(KtErrorsNoArg.kt:32) at org.jetbrains.kotlin.noarg.fir.FirNoArgExtensionRegistrar.configurePlugin(FirNoArgExtensionRegistrar.kt:15) at org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar.configuredExtensionFactories_delegate$lambda$0(FirExtensionRegistrar.kt:294) at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:86) at org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar.getConfiguredExtensionFactories(FirExtensionRegistrar.kt:291) at org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar.configure(FirExtensionRegistrar.kt:270) at org.jetbrains.kotlin.fir.session.FirAbstractSessionFactory.createSharedLibrarySession(FirAbstractSessionFactory.kt:107) at org.jetbrains.kotlin.fir.session.FirJvmSessionFactory.createSharedLibrarySession(FirJvmSessionFactory.kt:53) at org.jetbrains.kotlin.cli.pipeline.jvm.JvmFrontendPipelinePhase.prepareJvmSessions$lambda$0(JvmFrontendPipelinePhase.kt:326) at org.jetbrains.kotlin.cli.common.SessionConstructionUtils.prepareSessions(FirSessionConstructionUtils.kt:324) at org.jetbrains.kotlin.cli.pipeline.jvm.JvmFrontendPipelinePhase.prepareJvmSessions(JvmFrontendPipelinePhase.kt:322) at org.jetbrains.kotlin.cli.pipeline.jvm.JvmFrontendPipelinePhase.executePhase(JvmFrontendPipelinePhase.kt:137) at org.jetbrains.kotlin.cli.pipeline.jvm.JvmFrontendPipelinePhase.executePhase(JvmFrontendPipelinePhase.kt:47) at org.jetbrains.kotlin.cli.pipeline.PipelinePhase.phaseBody(PipelinePhase.kt:68) at org.jetbrains.kotlin.cli.pipeline.PipelinePhase.phaseBody(PipelinePhase.kt:58) at org.jetbrains.kotlin.config.phaser.NamedCompilerPhase.invoke(CompilerPhase.kt:102) at org.jetbrains.kotlin.backend.common.phaser.CompositePhase.invoke(PhaseBuilders.kt:22) at org.jetbrains.kotlin.config.phaser.CompilerPhaseKt.invokeToplevel(CompilerPhase.kt:53) at org.jetbrains.kotlin.cli.pipeline.AbstractCliPipeline.runPhasedPipeline(AbstractCliPipeline.kt:109) at org.jetbrains.kotlin.cli.pipeline.AbstractCliPipeline.execute(AbstractCliPipeline.kt:68) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecutePhased(K2JVMCompiler.kt:79) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecutePhased(K2JVMCompiler.kt:45) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:90) at org.jetbrains.kotlin.cli.common.CLICompiler.exec(CLICompiler.kt:352) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1617) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:569) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200) at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197) at java.base/java.security.AccessController.doPrivileged(AccessController.java:712) at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:840)Caused by: java.lang.ClassNotFoundException: org.jetbrains.kotlin.com.intellij.psi.PsiElement at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:592) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525) ... 42 more
-
미해결모던 안드로이드 - Jetpack Compose 입문
onTabFavorite 콜백 관련 질문
안녕하세요,강의에 구성하신 콜백 메서드 대신 아무 파라미터도 받지 않고 부모에서 정의한 isFavorite을 바꿔주는 형태로 콜백을 구성해도 되는것인지 궁금합니다. class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { MyApplicationTheme { Scaffold { innerPadding -> Box( modifier = Modifier .padding(innerPadding) .fillMaxSize() ) { var isFavorite by rememberSaveable { mutableStateOf(true) } ImageCard( modifier = Modifier .fillMaxWidth(0.5f) .padding(16.dp), isFavorite ) { isFavorite = !isFavorite } } } } } } } @Composable fun ImageCard( modifier: Modifier = Modifier, isFavorite: Boolean, onTabFavorite: () -> Unit, ) { Card( shape = RoundedCornerShape(8.dp), elevation = CardDefaults.cardElevation(5.dp), ) { Box( modifier = Modifier.height(200.dp) ) { Image( painter = painterResource(R.drawable.moon), contentDescription = "cute", contentScale = ContentScale.Crop, ) Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopEnd ) { IconButton( onClick = onTabFavorite, ) { Icon( imageVector = if (isFavorite) Icons.Default.Favorite else Icons.Default.FavoriteBorder, contentDescription = "favorite", tint = Color.White, ) } } } } }감사합니다!
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
결제 관련 서킷 브레이커 전략, 데이터 정합성 및 타임아웃 설정 질문
안녕하세요, 제미니님!강의 완독 후, 결제 시스템의 안정성을 높이기 위해 개인 프로젝트에 외부 PG사 연동 구간에 장애 격리 처리를 직접 구현하며 경험하고 있습니다. 구현 과정에서 고민했던 설계 내용과 제 나름의 가설이 맞는지, 그리고 기존 강의 코드의 설정 의도에 대해 여쭤보고 싶습니다. 🙇🏻♂️ 1. 서킷 브레이커 도입 및 트랜잭션 분리 전략 @Component class BeeceptorPaymentClient( private val restClient: RestClient, private val circuitBreaker: CircuitBreaker, ) : PaymentClient { private val log = LoggerFactory.getLogger(javaClass) override fun requestPayment(command: PaymentCommand): PaymentResult { return circuitBreaker.run("beeceptor-payment") { executePayment(command) } .fallbackIfOpen { log.warn("[Circuit Open] Beeceptor 결제 서비스 차단됨. 잠시 후 재시도 필요.") throw CoreException(ErrorType.PAYMENT_EXTERNAL_API_UNAVAILABLE) } .getOrElse { e -> when (e) { is HttpClientErrorException -> { log.warn("[PAYMENT_REJECTED] 결제 거절: ${e.responseBodyAsString}") throw CoreException(ErrorType.PAYMENT_REJECTED) } is CoreException -> throw e else -> { log.error("[PAYMENT_FAILED] Beeceptor 결제 호출 실패", e) throw CoreException(ErrorType.PAYMENT_EXTERNAL_API_FAIL) } } } } }외부 PG사 장애 전파를 막기 위해 Resilience4j를 도입했고, 테스트를 위해 Beeceptor를 활용했습니다.설정 전략:COUNT_BASED (최소 10회, 실패율 50% Open), 단순 비즈니스 에러(4xx)는 ignoreExceptions로 제외하여 시스템 장애만 감지하도록 설정했습니다. 트랜잭션 분리(Facade 패턴): 트랜잭션에 대한 DB 커넥션 점유 시간을 최소화하기 위해 아래와 같이 로직을 3단계로 분리했습니다. 1. 검증: DB 조회 (ReadOnly 트랜잭션)2. 외부 요청: 외부 PG API 호출 (No 트랜잭션 + 서킷 브레이커 적용)3. 상태 업데이트: 결제 결과 저장 (Write 트랜잭션)@Component class PaymentConfirmFacade( private val paymentService: PaymentService, private val paymentClient: PaymentClient, ) { fun success(orderKey: String, externalPaymentKey: String, amount: BigDecimal): Long { // 1. [DB Transaction] 결제 검증 val command = paymentService.validatePayment(orderKey, externalPaymentKey, amount) // 2. [No Transaction] 외부 API 호출 val paymentResult = paymentClient.requestPayment(command) // 3. [DB Transaction] 결제 완료 처리 // 외부 API에서 받은 결과(transactionId 등)를 넘겨줌 return paymentService.completePayment(orderKey, paymentResult) } } [질문] Facade 패턴을 적용함에 따라 '외부 요청'(2번)은 성공했으나, '상태 업데이트 및 저장'(3번)에서 DB 장애 등으로 실패할 경우 데이터 불일치가 발생합니다. 현재 로직상 completePayment가 실패하면 트랜잭션이 롤백되어 TransactionHistory조차 남지 않고 Payment 상태는 READY로 유지됩니다.따라서 저는 배치/스케줄러를 통해 일정 시간이 지나도 READY 상태로 남아있는 결제 건들을 조회한 뒤, PG사 결제 내역 조회 API와 크로스 체크하여 누락된 결제를 보정하는 로직이 필요하다고 판단됩니다. 이러한 접근 방식이 실무에서사용하는 일반적인 보정 패턴인지 궁금합니다. 2. DB Connection/Socket Timeout 설정 의도와 Facade 적용 시의 상관관계외부 API 타임아웃(Connect 3s, Read 30s)을 학습하며 적용하던 중, 기존 강의 프로젝트의 db-core.yml 설정이 눈에 들어왔습니다.connection-timeout: 1100 (1.1초)socketTimeout: 3000 (3초)[질문 1] 보통 DB 타임아웃을 넉넉하게 잡는 경우도 있는데, 이렇게 타이트하게 설정하신 의도가 "트래픽 급증 시 DB 커넥션을 얻지 못하면 빠르게 실패 처리하여 스레드 풀 고갈을 막고 시스템 전체 장애를 방지하기 위함" 인지, 혹은 다른 운영 노하우가 담겨 있는 것인지 궁금합니다.[질문 2] 저는 Facade 패턴을 적용하여 외부 API 호출 시점에는 트랜잭션(DB Connection)을 점유하지 않도록 분리했습니다. 따라서 기술적으로는 DB 타임아웃과 서킷 브레이커(외부) 타임아웃을 서로 독립적인 관점으로 설정해도 된다고 판단했습니다.다만, 실무 운영 관점에서는 결국 '전체 사용자 대기 시간(User Latency)' 이라는 제약이 존재할 텐데, 이때 전체 응답 시간 제한 내에서 DB와 외부 API 타임아웃 비중을 어떻게 배분하시는지 강사님만의 기준이나 노하우가 궁금합니다 ! 긴 글 읽어주셔서 감사합니다.
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
비회원 개념 추가 시 개선 방향
요즘 쇼핑몰 커머스등은 비회원 주문이 있는 경우가 거의 대부분인 것 같은데 비회원 주문,결제의 개념이 추가된다면 어떻게 개선될 수 있을까요? api가 복제되는 느낌으로 가야하는지.. 주문, 장바구니, 결제 등등 유저아이디 대신 게스트아이디로 조회하는 등과 같이 작은 부분만 바뀌고 나머지 로직은 동일 반복될 것 같습니다.주문조회시에도 회원 비회원.. 서비스는하나를 쓰고 OrderFinder에서 함수로 나누는 방식도 생각납니다.어떤 방식이 더 있을까요?