묻고 답해요
169만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨Spring Boot, AWS로 백엔드 서비스 한 사이클 완성하기
JPA Repository 질문이 있습니다!
안녕하세요!기존 PostRepository를 사용하다가 JPA 도입으로 JpaPostRepository가 새로 생겼는데, JpaPostRepository 클래스에 재구현을 하기 위해 default 메서드를 추가했는데 그 방법이 아닌 PostRepositoryImpl 와 같은 구현체를 만들어서 구현해도 무방할까요? 실무에서는 어떤 방식이 더 자주 사용 되는지 궁금합니다.
-
해결됨Spring Boot, AWS로 백엔드 서비스 한 사이클 완성하기
페이지네이션 처리를 쿼리에서 하는 방식 질문
안녕하세요. 강의 잘 보고 있습니다.여기서 페이지네이션 구현을 values 데이터값을 가져와서 Java단 코드내에서 stream을 사용하여 페이징 처리를 하고 있는데, 만약 데이터를 DB에서 가져온 데이터라면 쿼리에서 페이징 처리(limit, offset)를 하는게 더 효율적일까요?
-
해결됨Spring Boot DDD 실전: 주문 시스템으로 배우는 도메인 설계
DDD 는 마이바티스와 잘 맞지 않는건가요?
안녕하세요.DDD 관련 강의나 블로그를 보면대부분 JPA 로 설명해주시더라구요. 마이바티스도 실무에서 오히려 더 폭 넒게 쓰이는 것 같은데 마이바티스로는 DDD 를 적용하기 어려운걸까요?
-
해결됨Spring Boot DDD 실전: 주문 시스템으로 배우는 도메인 설계
스프링부트 버전 문의드립니다.
안녕하세요. 부트버전을 3.5 로 설정해서 수강해도 괜찮을까요. 회사에서 아직 4.x 버전 업 계획이 없어서요.
-
미해결죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.
Json 요청 처리
형 나 지금 너무 재밌어서 잘 따라하던중 별거 아닌거에서 막혀서 좀 힘들어형이 알려준 json 파라미터 넘기는 방법으로 해도 안되고 gpt 찾아서 진행 한거도 다 안돼는데 윈도우 환경에서 좀 힘든걸까 ?? 다른 수강생분이 올려준거도 봤는데 답변에 달아준 방법도 동작하지 않아 @Bean public JsonJobParametersConverter jobParametersConverter() { return new JsonJobParametersConverter(); } @Bean @StepScope public Tasklet terminatorTasklet( @Value("#{jobParameters['infiltrationTargets']}") String infiltrationTargets ) { return (contribution, chunkContext) -> { String[] targets = infiltrationTargets.split(","); log.info("⚡ 침투 작전 개시"); log.info("첫 번째 타겟: {} 침투 시작", targets[0]); log.info("마지막 타겟: {} 에서 집결", targets[1]); log.info("🎯 임무 전달 완료"); return RepeatStatus.FINISHED; }; }
-
미해결백엔드 개발자 성능 개선 초석 다지기
비동기 스레드풀 분리 이유와 Virtual Thread 전환 시 고려사항
안녕하세요! 좋은 강의 잘 듣고 있습니다.CompletableFuture.runAsync()에 커스텀 Executor를 따로 넘기는 코드를 보면서 궁금한 점이 생겼습니다.동기 방식은 어차피 요청 스레드에서 직접 처리되니까 별도 스레드풀 설정이 의미 없는 거라 비동기에서만 설정하는 건가요? 아니면 동기에서도 풀을 따로 구성하는 케이스가 있는지 궁금합니다.그리고 Java 21부터는 Executors.newVirtualThreadPerTaskExecutor()가 기존 플랫폼 스레드풀을 대체하는 권장 방식인지도 여쭤보고 싶습니다. Virtual Thread는 풀링 없이 매 작업마다 새로 생성하는 방식이라고 이해했는데, 실무에서 전환할 때 주의할 포인트가 있다면 함께 말씀해 주시면 감사하겠습니다!
-
미해결스프링 시큐리티 완전 정복 [6.x 개정판]
로그아웃-logout()-2 강에서 겟방식 로그아웃 호출 후 화면이동 질문입니다.
8분 29초에서 /logoutProc 를 겟방식으로 호출했고 로그아웃은 성공했습니다.로그아웃 성공후 /logoutSuccess 로 이동해야 하고 해당 url 은 permitAll() 입니다. 그런데 왜 로그인화면이 표시되는지 궁금합니다.강의를 조금 더 보다보니, 궁금증은 해결됐습니다. 그런데 여전히 궁금한 점은 해당 소스에 맨 아래 permitAll() 의 역할입니다. 실제 아무일도 안하는건지 궁금합니다.
-
해결됨The 10x AI-Native Developer: 회사에서 AI로 압도적 성과를 내는 법
스타터패키지 받을 수 있는 기회가 있을까요?
딩코딩코님, 혹시 리뷰이벤트 스타터패키지 파일 받을 수 있을까요?
-
해결됨카카오 면접관의 실무 밀착형 Spring Batch: 대용량 데이터 처리의 모든 것
job, step execution 관련 질문 드립니다.
안녕하세요.잡이 어떻게 스텝에서 사용하는 컨텍스트 값까지 가지고 있는지 잘 이해가 되지 않아 질문드립니다.분명 JobExecutionContext에 넣은 것이 아니라 StepExecutionContext에 값을 저장했는데, 확인해보니 JobExecutionContext에도 동일하게 저장된 것처럼 보여서 헷갈렸습니다.제가 이해한 바로는 JobExecutionContext와 StepExecutionContext는 서로 다른 영역이고,JobExecutionContext는 step 간 공유용, StepExecutionContext는 해당 step 전용으로 알고 있습니다.그런데 왜 StepExecutionContext에 넣은 값이 JobExecutionContext에도 같은 형태로 보이는지 잘 모르겠습니다.
-
해결됨제미니의 개발실무 - 커머스 백엔드 기본편
다양한 관점의 코드 경험을 위해 개선하지 않은 코드
안녕하세요. 제미니님 유튜브 부터 인프런까지 참여하며 굉장히 많은 인사이트를 얻고있어 무한한 감사 인사를 올립니다. 질문강의를 수강하며 제미니님이 던져준 키워드를 어떻게 곱씹어야하지? 라는 생각을 하며 두 가지 정도 질문을 드리게 되었습니다. Q1. "저같은 경우는 뭐 컴포넌트 같은 걸 좀 쪼개서 만들고 싶은데, 일단은 여러분들이 좀 혼합된 걸 느끼게 하려고 제가 풀어 놨어요" - 결제 코드 느끼기 13:17이렇게 제미니님이 생각했던 코드를 보고 싶은데, 이 코드는 신규 강의였던 "레거시 다루기" 에서 개선 작업을 하나요? 아니면 저희에게 열린 사고를 던져주고 넘어가는걸까요? Q2. "success 메서드에 트랜잭셔널을 사용하는 것도 할 말이 많은데 기본적인 로직에서는 문제는 없다." - 결제 코드 느끼기 13:58이 내용에서도 혹시 개선하는 부분도 질문 1번과 같이 레거시 다루기 강의에서 개선 하시나요?개인적으로 success 에서 트랜잭션 어노테이션을 빼고, 저장하는 로직을 한 군데 모아서 거기 사용할 것 같은데 제미니님은 어떻게 하시는지 궁금하네요!
-
해결됨The 10x AI-Native Developer: 회사에서 AI로 압도적 성과를 내는 법
1-7 강의 PreToolUse 동작 안함
PreToolUse 이 동작을 안합니다.setting.json에 SessionStart 적용.claudeignore 생성dispatcher.js 내 input을 try 밖에 선언위의 3가지 방법을 다 해봤는데도 동작을 안합니다.
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
sdk 설정 오류
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.이렇게 오류가 떠서 jdk oracle을 다운 받고sdk도 설정하고IVM도 설정했습니다. 자바 스프링 버전은 최신 버전인 4.0.5 대신 강의 자료에서 3.x인 최신버전을 선택하라길래 3.5.13.을 선택했습니다. 무슨 문제일까요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
consumer에서 에러가 발생할 경우 데이터 유실 문의
안녕하세요. kafka 관련해서 질문이 있습니다.MessageRelay > publishEvent 에서 outbox를 삭제했는데 consumer에서 에러가 발생하면 데이터가 유실되는게 아닌가요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
게시글 테스트 데이터 삽입
DataInitializer 자체가 이해가 안되는게 있습니다.Test 환경에서 @Autowired 가 가능한건지 궁금하네요
-
해결됨카카오 면접관이 알려주는 MSA 관점에서의 분산 트랜잭션 패턴
Orchestration SAGA 패턴 보상에 대한 질문입니다.
Orchestration SAGA 패턴 구현에 대해 고민하다가 질문이 생겨 남깁니다.보상을 요청하는 메서드가 명시적으로 나와있어 호출할 때(동기로 호출)만약 rollback을 요청하는 호출이 실패하게 된다면 이후의 순서대로 service에 보상 요청을 하는 동작을 멈춰야 할지 계속 진행하는 게 바람직할지 고민이 됩니다.예를 들어 서비스 1,2,3,4가 있고 center server가 orchestration 관리를 하고 1,2,3,4 순서대로 서비스 호출해서 관리를 진행한다고 했을 때 3에서 장애가 발생해서 2를 롤백하던 중 2 롤백에서 예외가 발생해서 롤백에 실패한 경우 orchestration에서 1에 대한 롤백을 진행해줘야 할지 아니면 일단 멈춰야 할지 고민입니다. 고민의 이유는 순서대로 롤백을 해주는 것은 앞에 작업이 뒤의 작업에 의존성이 있을 때만 그렇게 해주면 되나에 대한 고민이 있었습니다. 두 롤백 간에 데이터 의존성이 없다면 괜찮지 않을까 고민했습니다.다음으로 일단 1도 롤백을 한다면 어디서부터 어디까지 롤백이 진행됐는지 추적이 어려워지지 않을까 고민했습니다. 롤백을 어떤 것은 해주고 어떤 것은 안해준다면 어디까지 롤백했는지 추적이 힘들어지지 않을까 생각이 들었습니다.
-
미해결죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.
[건의][6장][작전1] deprecated 메소드
형 ... @Bean public Step threatAnalysisStep( JpaPagingItemReader<Human> humanThreatDataReader, ItemProcessor<Human, TargetPriorityResult> threatAnalysisProcessor, FlatFileItemWriter<TargetPriorityResult> targetListWriter ) { return new StepBuilder("threatAnalysisStep") .<Human, TargetPriorityResult>chunk(10, transactionManager) .reader(humanThreatDataReader) .processor(threatAnalysisProcessor) .writer(targetListWriter) .taskExecutor(taskExecutor()) // deprecated 되어서 안씀 // .throttleLimit() .build(); } ... 에서 .throttleLimit() 부분이 5 이후로 deprecated 된다고 명시되어 있어. 관심사 분리로 인해서 taskExecutor 정의 부분에서 설정하라고 권장하는 것을 확인했거든. 현재 강의가 사실상 Spring Batch 5.x 까지의 기본 내용과 원리를 살펴봤잖아 ? 그렇다 하더라도 혼란을 줄 수 있기 때문에 해당 부분은 삭제하던지 아니면 추가 내용을 기재해야 할 것으로 보여.
-
미해결죽음의 Spring Batch: 새벽 3시의 처절한 공포는 이제 끝이다.
[예제][3장][작전2] windows 에서 마지막 예제
... @Slf4j @Configuration @RequiredArgsConstructor public class LogProcessingJobConfig { private final JobRepository jobRepository; private final PlatformTransactionManager transactionManager; private final String BASE_PATH = "C:\\Users\\user\\Desktop\\batch\\section3\\flatfileitemwriter"; @Bean public Job logProcessingJob( Step createDirectoryStep, Step logCollectionStep, Step logProcessingStep ) { return new JobBuilder("logProcessingJob", jobRepository) .start(createDirectoryStep) .next(logCollectionStep) .next(logProcessingStep) .build(); } @Bean public Step createDirectoryStep(SystemCommandTasklet mkdirTasklet) { return new StepBuilder("createDirectoryStep", jobRepository) .tasklet(mkdirTasklet, transactionManager) .build(); } @Bean @StepScope public SystemCommandTasklet mkdirTasklet( @Value("#{jobParameters['date']}") String date ) { SystemCommandTasklet tasklet = new SystemCommandTasklet(); tasklet.setWorkingDirectory(BASE_PATH); String collectedLogsPath = "collected_ecommerce_logs\\" + date; String processedLogsPath = "processed_logs\\" + date; try { tasklet.setCommand("cmd.exe", "/c", "mkdir", collectedLogsPath, processedLogsPath); tasklet.setTimeout(3000); } catch (Exception e) { log.error("mkdirTasklet error: ", e); } return tasklet; } @Bean public Step logCollectionStep(SystemCommandTasklet scpTasklet) { return new StepBuilder("logCollectionStep", jobRepository) .tasklet(scpTasklet, transactionManager) .build(); } @Bean @StepScope public SystemCommandTasklet scpTasklet( @Value("#{jobParameters['date']}") String date ) { SystemCommandTasklet tasklet = new SystemCommandTasklet(); tasklet.setWorkingDirectory(BASE_PATH); String processedLogsPath = "collected_ecommerce_logs\\" + date; StringJoiner commandBuilder = new StringJoiner(" & "); for (String host : List.of("localhost")) { // Windows 환경에서 해당 코드를 적용하려면 ssh 관련 설정들을 찾아야 해서 주석 처리 // String command = String.format("scp %s:~/ecommerce_logs/%s.log %s\\%s.log", host, date, processedLogsPath, host); String sourcePath = BASE_PATH + "\\ecommerce_logs\\" + date + ".log"; String command = String.format("copy %s %s\\%s.log", sourcePath, processedLogsPath, host); commandBuilder.add(command); } tasklet.setCommand("cmd.exe", "/c", commandBuilder.toString()); tasklet.setTimeout(10000); return tasklet; } @Bean public Step logProcessingStep( MultiResourceItemReader<LogEntry> multiResourceItemReader, LogEntryProcessor logEntryProcessor, FlatFileItemWriter<ProcessedLogEntry> processedLogEntryJsonWriter ) { return new StepBuilder("logProcessingStep", jobRepository) .<LogEntry, ProcessedLogEntry>chunk(10, transactionManager) .reader(multiResourceItemReader) .processor(logEntryProcessor) .writer(processedLogEntryJsonWriter) .build(); } @Bean @StepScope public MultiResourceItemReader multiResourceItemReader( @Value("#{jobParameters['date']}") String date ) { MultiResourceItemReader<LogEntry> resourceItemReader = new MultiResourceItemReader<>(); resourceItemReader.setName("multiResourceItemReader"); resourceItemReader.setResources(getResources(date)); resourceItemReader.setDelegate(logFileReader()); return resourceItemReader; } private Resource[] getResources(String date) { try { String formattedBasePath = BASE_PATH.replace("\\", "/"); String location = "file:///" + formattedBasePath + "/collected_ecommerce_logs/" + date + "/*.log"; PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); return resolver.getResources(location); } catch (IOException e) { throw new RuntimeException("Failed to resolve log files", e); } } @Bean public FlatFileItemReader<LogEntry> logFileReader() { return new FlatFileItemReaderBuilder<LogEntry>() .name("logFileReader") .delimited() .delimiter(",") .names("dateTime", "level", "message") .targetType(LogEntry.class) .build(); } @Bean public LogEntryProcessor logEntryProcessor() { return new LogEntryProcessor(); } @Bean @StepScope public FlatFileItemWriter<ProcessedLogEntry> processedLogEntryFlatFileItemWriter( @Value("#{jobParameters['date']}") String date ) { String outputPath = Paths.get(BASE_PATH, "processed_logs", date, "processed_logs.jsonl").toString(); ObjectMapper objectMapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))); objectMapper.registerModule(javaTimeModule); return new FlatFileItemWriterBuilder<ProcessedLogEntry>() .name("processedLogEntryJsonWriter") .resource(new FileSystemResource(outputPath)) .lineAggregator(item -> { try { return objectMapper.writeValueAsString(item); } catch (JsonProcessingException e) { throw new RuntimeException("Error Converting item to JSON", e); } }) .build(); } @Data public static class LogEntry { private String dateTime; private String level; private String message; } @Data public static class ProcessedLogEntry { private LocalDateTime dateTime; private LogLevel level; private String message; private String errorCode; } public enum LogLevel { INFO, WARN, ERROR, DEBUG, UNKNOWN; public static LogLevel fromString(String level) { if (level == null || level.trim().isEmpty()) { return UNKNOWN; } try { return valueOf(level.toUpperCase()); } catch (IllegalArgumentException e) { return UNKNOWN; } } } public static class LogEntryProcessor implements ItemProcessor<LogEntry, ProcessedLogEntry> { private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_DATE_TIME; private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("ERROR_CODE\\[(\\w+)]"); @Override public ProcessedLogEntry process(LogEntry item) throws Exception { ProcessedLogEntry processedLogEntry = new ProcessedLogEntry(); processedLogEntry.setDateTime(parseDateTime(item.getDateTime())); processedLogEntry.setLevel(parseLevel(item.getLevel())); processedLogEntry.setMessage(item.getMessage()); processedLogEntry.setErrorCode(extractErrorCode(item.getMessage())); return processedLogEntry; } private LocalDateTime parseDateTime(String dateTime) { return LocalDateTime.parse(dateTime, ISO_FORMATTER); } private LogLevel parseLevel(String level) { return LogLevel.fromString(level); } private String extractErrorCode(String message) { if (message == null) { return null; } Matcher matcher = ERROR_CODE_PATTERN.matcher(message); if (matcher.find()) { return matcher.group(1); } // ERROR 문자열이 포함되어 있지만 패턴이 일치하지 않는 경우 if (message.contains("ERROR")) { return "UNKNOWN_ERROR"; } return null; } } } 윈도우즈에서는 scp 명령어 관련해서 windows 서비스에서 open-ssh-server 실행현재 사용하고 있는 사용자(windows 사용자) 가 관리자 권한을 보유 할 경우 programdata/sshd_config 파일 수정 필요기타 파일 권한 정리 필요로 인하여 scp 프로그램을 windows 환경의 intellij idea 에서 id, 비번치게끔 ide 콘솔에서 지원하지 않는거 같아. 그래서 제미니가 추천해준대로 실습 환경에서 scp 접속 단을 copy 로 그냥 퉁치는 것으로 바꿔놨어. 나처럼 windows 환경인 사람들이 있으면 혹시나 참고했으면 좋겠네. ObjectMapper, JavaTimeModule 같은 경우는 org.springframework.boot:spring-boot-starter-json 의존성 추가 필요
-
해결됨The 10x AI-Native Developer: 회사에서 AI로 압도적 성과를 내는 법
업데이트 관련 문의입니다.
안녕하세요.26/1/17 1-3 claude memory 기능 변경 업데이트위의 업데이트 되는 내용들은 어디서 확인할 수 있을까요?
-
해결됨서버개발자 과제전형 완벽가이드 - 1편
NaverBookRepository.class 의 위치에 따른 모듈간의 의존성에 대해 질문드립니다.
학습관련 질문을 남겨주세요! 상세히 작성주시면 더 좋아요 🙂 안녕하세요, 강사님 🙂이번 질문도 혼자 먼저 구현 후, 강사님의 강의를 들으며 리팩토링을 진행하며 궁금증이 하나 생겨 질문 드립니다. 저는 search-api 모듈의 com.library.repository의 NaverBookRepository.class 위치를 강사님과 다르게 external:naver-client 에 위치시켰습니다.(NaverBook/KakaoBook은 external 에 종속된 구현체라고 생각) 그러다 보니, naver-client 모듈이 search-api 모듈에 있는 BookRepository, PageResponse, SearchResponse 를 알아야 했으며, naver-client 모듈이 search-api 모듈을 implementation('...') 으로 의존?참조? 하게 되었습니다. // search-api 모듈 public interface BookRepository { PageResponse<SearchResponse> search(...); } 이렇게 external -> search-api 로 흘러가는 의존 방향에 이질감이 듭니다.(외부 API 가 app 을 안다..?) 그래서 모듈간 의존 방향이 search-api -> external 되도록 강사님 강의처럼 NaverBookRepository 를 search-api 모듈에 위치 시켜도 NaverClient 때문에 search-api에 feign 종속성이 생긴 다는 것이 모듈간 의존성 격리가 되지 않아서 멀티모듈의 장점이 퇴색되었다고 생각합니다. 강사님은 과제가 아닌 실무에서 search-api 와 external 간의 모듈 격리를 어떻게 하시며, 의존 방향을 어떻게 하시는지 궁금합니다. 저의 부족한 지식을 확장할 수 있게 강사님의 인사이트 공유해주시면 감사하겠습니다!
-
미해결실전! 스프링부트 상품-주문 API 개발로 알아보는 TDD
POJO에서 Spring Test로 넘어갈 때 누락(해결됨)
안녕하세요 강의 정말 잘 듣고 있습니다현재 코틀린으로 직접 타이핑해보며 따라하고 있는데, 자바 공부에 대한 필요성을 느끼고 있습니다다름이 아니라 섹션 2의 POJO에서 스프링 테스트로 전환하며, 기존 테스트 디렉토리의 ProductServiceTest에 대부분 코드가 몰려 있던 구조에서 main 디렉토리에 파일이 많이 늘어서 구조를 맞추는 데 좀 어려웠습니다 중간에 누락된 부분이 있을까요? 만약 같은 분이 계시다면, 클래스명 선택한 다름(드래그로 하이라이트) f5가 이동, f6이 복사로 해당 클래스를 메인에 옮길 수 있습니다(윈도우/리눅스 인텔리제이 기준)확인했습니다! POJO에서 이미 제가 말씀드린 방법으로 리팩토링 수행하셨네요혹시나 헷갈리시는 분들 계실까봐 남겨두겠습니다