묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
섹션4 (좋아요)의 댓글 수 구현 강의에서 질문 있습니다.
안녕하세요 ! 좋은 강의 해주셔서 감사합니다.섹션 4의 댓글 수 구현 강의 3분 39초 경에서 질문 있습니다 .선생님의 코드에서는 댓글을 물리적 삭제할때만 댓글 수를 decrease를 통해 줄여주고 있는데, 논리적 삭제 시에도 count를 줄여주는 것이 맞다고 생각해서 질문글 남깁니다. 왜 물리적 삭제 시에만 댓글 수를 decrease 하시는 건가요 ?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
ArticleDeletedEventHandler
handle()에 조회 수, 좋아요 수, 댓글 수 캐시도 삭제해주어야 하는데 누락되어야 한다고 생각이 하는데 어떻게 생각하시는지 궁금합니다! 아니면 다른 의도가 있으셨을까요?강의에서는 생성 시간 및 인기글 캐시만 삭제하고 있습니다!
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
TransactionalEventListener 관련 문의드리고자 글 남깁니다.
안녕하세요, 강사님 늘 강의 잘 듣고 있습니다, 감사합니다 !다름이 아니고, @Transactional과 @TransactionalEventListener 설정 관련해 궁금한 사항이 있어서 문의드리게 되었습니다.강의내용은 @Transactional, @TransactionalEventListener가 전역적으로 선언되었고 outbox 관련 로직만 존재하기 때문에 괜찮지만제 업무환경에서는 @TrasnactionalEventListener가 사용되면 프로젝트의 모든 @Transactional으로 관련된 내용이 바인딩되기 때문에 이를 scope 별로 구분하는 방법이 필요해보였는데요. 따로 알아보았지만, payload(강의에서는 OutboxEvent) 를 통해 분기처리하는 방법만 보이다보니 혹시 관련하여 TrasnactionalEventListener 어노테이션 필드 혹은 커스텀 어노테이션만으로 구분할 수 있는 방법이 있을까요 ?문의드리는 가장 큰 이유는 outbox 로직이 필요없는 DB처리에도 TrasnactionalEventListener이 우선은 호출되고 payload를 통해 early return 시키려고 하다보니, 불필요한 리소스 사용을 방지하고 싶어서 문의드리게 되었습니다.
-
해결됨실리콘밸리 엔지니어와 함께하는 Redis
리뷰 이벤트로 수강했던 강의 철회 관련 재요청 될까요?
안녕하세요.예전에 리뷰 이벤트를 신청했었던 사람이니다.당시 구매한 강의 3개(레디스, 스칼라, 그래들)를 수강했던 수강생입니다.당시 리뷰 이벤트로 강의를 제공받은 후, 개인적인 오해와 감정적인 대응으로 인해 영자 측에 해당 강의들 모두 철회 요청을 드렸었습니다. 지금 돌이켜보면 너무 감정적으로 행동했던 것 같고, 다소 어리석은 선택이었음을 느끼고 있습니다.그 후 지식공유자님께도 별도로 사과의 말씀을 전한 적이 있으며, 그때의 제 행동에 대해 진심으로 반성하고 있습니다.이메일 cis940320@gmail.com 으로 신청을 드렸었고, 해당 메일 내역을 통해 당시 리뷰와 신청한 강의 내역도 확인 가능하실 것이라 생각됩니다.혹시 가능하다면, 철회했던 강의들을 다시 수강할 수 있는 기회를 부탁드릴 수 있을까요?당시에 제가 작성했던 리뷰도 정성스럽게 남겼던 기억이 있어, 다시 한 번 수강할 수 있게 된다면 더욱 성실하게 공부하고 의미 있게 활용하고 싶습니다.번거롭게 해드려 정말 죄송하고, 답변 기다리겠습니다.감사합니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
오타 문의
안녕하세요! 우선 강의 너무 잘 듣고 있습니다!강의 자료에서 좋아요 수 설계 부분에서11:171:1 관계이지만, 테이블 비정규화의 필요성을 살펴본 것이다.이 부분은 1:1 관계이므로 비정규화의 필요성이 있지만, 레코드 락으로 인한 자원 한계를 고려하여 '정규화'의 필요성을 살펴본 것 이 맞지 않나 싶습니다! 혹시 제가 잘못 이해한 부분이 있다면 알려주시면 감사하겠습니다!
-
해결됨비전공자도 이해할 수 있는 Redis 입문/실전 (조회 성능 최적화편)
Nest.js + Redis 구현 문제
안녕하세요레디스 강의를 재미있게 수강하고 있는데요문제가 있어서 여러가지 찾아보았지만 잘 해결이 되지 않아서 여기에 문의 남깁니다 import { Module } from '@nestjs/common'; import { BoardController } from './board.controller'; import { BoardService } from './board.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Board } from './board.entity'; import { CacheModule } from '@nestjs/cache-manager'; import * as redisStore from 'cache-manager-ioredis'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: '', // 임시로 삭제 password: '', // 임시로 삭제 database: 'mydb', autoLoadEntities: true, synchronize: true, // Note: set to false in production }), TypeOrmModule.forFeature([Board]), CacheModule.register({ store: redisStore, host: 'localhost', port: 6379, ttl: 60, }), ], controllers: [BoardController], providers: [BoardService], }) export class AppModule {} import { Inject, Injectable } from '@nestjs/common'; import { Board } from './board.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; @Injectable() export class BoardService { constructor( @InjectRepository(Board) private boardRepository: Repository<Board>, @Inject(CACHE_MANAGER) private cacheManager: Cache, ) {} async getBoards(page: number, size: number): Promise<Board[]> { const cacheKey = `boards:page:${page}:size:${size}`; const cachedData = await this.cacheManager.get<Board[]>(cacheKey); if (cachedData) { return cachedData; } const skip = (page - 1 ) * size; const boards = await this.boardRepository.find({ order : {created_at: 'desc'}, skip: skip, take: size, }); await this.cacheManager.set(cacheKey, boards); const cachedData3 = await this.cacheManager.get(cacheKey); console.log('stores:', this.cacheManager.stores); console.log('stores 타입:', typeof this.cacheManager.stores); console.log('stores 내용:', JSON.stringify(this.cacheManager.stores, null, 2)) return boards; } } 현재 이렇게 2개의 파일을 레디스 적용하기 위해서 작성을 하였는데요레디스에서 검색을 하면 정상적으로 조회가 되지 않습니다(mysql에서 가지고 온 데이터는 정상적으로 조회가 됩니다) 그래서 중간에 로그도 찍어보았는데 레디스가 아니라 keyv 여기에 저장이 되는거 같습니다왜 그런지 원인을 알고 싶습니다 참고로 레디스 cli, 서버는 정상적으로 동작 중 입니다이유는 스프링으로 레디스 연동은 정상적으로 잘 됩니다
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
406 Not Acceptable에러 발생
현재 테스트 코드 작성하고 있는데 다음과 같이 에러 발생합니다.코드package board.article.api; import board.article.service.request.ArticleCreateRequest; import board.article.service.response.ArticleResponse; import lombok.AllArgsConstructor; import lombok.Getter; import org.junit.jupiter.api.Test; import org.springframework.web.client.RestClient; public class ArticleApiTest { RestClient restClient = RestClient.create("http://localhost:9000"); @Test void createTest() { ArticleResponse response = create(new ArticleCreateRequest( "hi", "my content", 1L, 1L )); System.out.println("response = " + response); } ArticleResponse create(ArticleCreateRequest request) { return restClient.post() .uri("/v1/articles") .body(request) .retrieve() .body(ArticleResponse.class); } @Getter @AllArgsConstructor static class ArticleCreateRequest { private String title; private String content; private Long writerId; private Long boardId; } @Getter @AllArgsConstructor static class ArticleUpdateRequest { private String title; private String content; } } 에러 > Task :common:snowflake:compileJava UP-TO-DATE> Task :common:snowflake:processResources NO-SOURCE> Task :common:snowflake:classes UP-TO-DATE> Task :common:snowflake:jar UP-TO-DATE> Task :service:article:compileJava> Task :service:article:processResources UP-TO-DATE> Task :service:article:classes> Task :service:article:compileTestJava> Task :service:article:processTestResources NO-SOURCE> Task :service:article:testClassesorg.springframework.web.client.HttpClientErrorException$NotAcceptable: 406 Not Acceptable: "{"timestamp":"2025-05-29T06:00:40.838+00:00","status":406,"error":"Not Acceptable","path":"/v1/articles"}" at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:118) at org.springframework.web.client.StatusHandler.lambda$defaultHandler$3(StatusHandler.java:86) at org.springframework.web.client.StatusHandler.handle(StatusHandler.java:146) at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.applyStatusHandlers(DefaultRestClient.java:672) at org.springframework.web.client.DefaultRestClient.readWithMessageConverters(DefaultRestClient.java:195) at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.readBody(DefaultRestClient.java:659) at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.body(DefaultRestClient.java:605) at board.article.api.ArticleApiTest.create(ArticleApiTest.java:26) at board.article.api.ArticleApiTest.createTest(ArticleApiTest.java:15) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)> Task :service:article:testArticleApiTest > createTest() FAILED org.springframework.web.client.HttpClientErrorException$NotAcceptable at ArticleApiTest.java:261 test completed, 1 failed> Task :service:article:test FAILEDFAILURE: Build failed with an exception.* What went wrong:Execution failed for task ':service:article:test'.> There were failing tests. See the report at: file:///C:/Users/zxc86/Desktop/study/springboot/board/service/article/build/reports/tests/test/index.html* Try:> Run with --scan to get full insights.BUILD FAILED in 4s6 actionable tasks: 3 executed, 3 up-to-dategpt에서는 accept 설정이 없어서 그렇다는데 강사님 코드에는 없어서요원인이 뭘까요?
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
Redis 캐싱 시 발생하는 대표 문제 사례와 해결책 3 강의가 누락된 거 같습니다.
Redis 캐싱 시 발생하는 대표 문제 사례와 해결책 3 강의가 없습니다.바로 6주차 숙제로 넘어가네요
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
인덱스 데이터 흐름
1. 현재 학습 진도몇 챕터/몇 강을 수강 중이신가요?3-6. 쿼리플랜이란? 여기까지 이해하신 내용은 무엇인가요?옵티마이저가 스토리지 엔진으로부터 가장 효율적으로 데이터를 가져오기 위해 쿼리플랜을 작성한다는 것을 이해했습니다. 2. 어려움을 겪는 부분어떤 개념이 헷갈리시나요?이때 인덱스를 참고해서 데이터를 가져온다면 이 인덱스는 메모리에 올라와 있는 상태인가요?? 아니면 디스크에 있는 인덱스를 확인하고 디스크에서 데이터를 가져오는 건가요? 데이터를 가져오는 전체적인 흐름이 궁금합니다! 3. 시도해보신 내용문제 해결을 위해 어떤 시도를 해보셨나요?에러가 발생했다면 어떤 에러인가요?현재 작성하신 코드를 공유해주세요 이렇게 구체적으로 알려주시면, 더 정확하고 도움이 되는 답변을 드릴 수 있습니다!
-
해결됨6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법
cache eviction 관련 질문입니다.
위의 내용과 같이 언제, 어떻게 만료 시킬 지에 대한 만료 방식을 cache eviction이라고 해주셨는데 검색해보니까 내용 상의 차이가 있다는 것을 알게 됐습니다. TTL, TTI와 같은 특정 시간, 점유 시간에 대해 발동되어 cache expiration으로 정의가 됐습니다. 반면 cache eviction은 메모리 공간이 부족한 경우 발동되어 LRU, LFU, FIFO 등으로 구분됐습니다.결국 둘 다 캐시 삭제 기법이긴 하나, 트리거 조건에 차이가 있었습니다. 면접에서 eviction 관련 질문을 받는다면 둘을 혼용해도 무방할까요?
-
해결됨대기업 근무하며 경험한 Redis를 야무지게 사용하는 방법 [실습]
사용하시는 터미널 툴이름이 궁금합니다
안녕하세요 강의 내내 너무 편해보여서 질문올립니다 ㅠ 터미널이 다중으로 보이는데 툴 이름을 여쭤볼 수 있을까요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
안녕하세요 선생님 게시글 목록 API-페이지 번호 구현 강의에서 질문 있습니다.
안녕하세요 선생님 좋은 강의 감사합니다.저희 articleController에서 readAll 부분을 보면@GetMapping("/v1/articles") public ArticlePageResponse findAll( @RequestParam("boardId") Long boardId, @RequestParam("page") Long page, @RequestParam("pageSize") Long pageSize ) { return articleService.readAll(boardId, page, pageSize); }이런식으로 pageSize를 쿼리 파라미터로 넘겨주도록 설계가 되어 있는데, pageSize라는 파라미터는 값을 받아서 처리하는 것이 아니라, 서버에서 값을 정하고 항상 똑같은 pageSize를 써야하는 것 아닌가요 ??왜 pageSize를 인자로 받는지 이해가 잘가지 않습니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
안녕하세요 ! 강의 잘 듣고 있습니다.
안녕하세요 선생님 강의 잘 듣고 있습니다 .controller를 구현하실때 중복되는 부분을전부 다 쓰신 이유가 따로 있으신가요 ?예를 들어 ArticleController에서@RequestMapping("/v1/articles")public class ArticleController{...}이런식으로 RequestMapping 애노테이션을 붙여 중복을 줄일 수도 있는데 선생님은 전부 다 그냥 쓰셔서 어떤 의도가 있는 것인지가 궁금합니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
페이지네이션 시 조회 과정 질문
현재 페이지네이션 기반 조회 시 조회 과정이 궁금해서 질문드립니다.secondary index에서 offset 위치까지 순차탐색 -> secondary index -> 클러스터링 인덱스에서 primary key 조회 -> 실제 데이터 조회일단 위 과정이 맞는지 궁금합니다.2 . 위 과정이 맞다면 이 과정에서 db에서 데이터 조회를 limit만큼 반복하는 것인지, 아니면 실제 db에서 데이터 조회는 한 번에 이루어지는 것인지 궁금합니다.페이지 조회 말고 그냥 범위 조회일 시에는 한 번에 조회되서 값이 많아도 시간 차이가 많이 안나는지 궁금합니다. offset 없이 조회해보니 바로 조회되는 거 같긴합니다.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
값이 안변해요
학습 관련 질문을 최대한 상세히 남겨주세요!고민 과정도 같이 나열해주셔도 좋습니다.먼저 유사한 질문이 있었는지 검색해보세요.인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. @Test void updateTest() { update(184717674569289728L); ArticleResponse response = read(184717674569289728L); System.out.println("response = " + response); } void update(Long articleId) { restClient.put() .uri("/v1/articles/{articleId}", articleId) .body(new ArticleUpdateRequest("hi 2", "my content 22")) .retrieve(); } @Getter @AllArgsConstructor static class ArticleCreateRequest { private String title; private String content; private Long writerId; private Long boardId; } @Getter @AllArgsConstructor @NoArgsConstructor static class ArticleUpdateRequest { private String title; private String content; } response = ArticleResponse(articleId=184717674569289728, title=hi, content=my content, boardId=1, writerId=1, createdAt=2025-05-25T02:22:06, modifiedAt=2025-05-25T02:22:06) 이렇게 값이 안변하는 이유가 있을까요? @PutMapping("/v1/articles/{articleId}") public ArticleResponse update(@PathVariable Long articleId, @RequestBody ArticleUpdateRequest request) { return articleService.update(articleId, request); }
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
Event 클래스에 제네릭을 쓰신 이유가 궁금합니다.
EventPayload 를 제네릭으로 하셨는데 단순히 다형성 이용해서 하는 거랑 차이가 뭘까요?package kuke.board.common.event; import kuke.board.common.dataserializer.DataSerializer; import lombok.Getter; @Getter public class Event<T extends EventPayload> { private Long eventId; private EventType type; private T payload; public static Event<EventPayload> of(Long eventId, EventType type, EventPayload payload) { Event<EventPayload> event = new Event<>(); event.eventId = eventId; event.type = type; event.payload = payload; return event; } public String toJson() { return DataSerializer.serialize(this); } public static Event<EventPayload> fromJson(String json) { EventRaw eventRaw = DataSerializer.deserialize(json, EventRaw.class); if (eventRaw == null) { return null; } Event<EventPayload> event = new Event<>(); event.eventId = eventRaw.getEventId(); event.type = EventType.from(eventRaw.getType()); event.payload = DataSerializer.deserialize(eventRaw.getPayload(), event.type.getPayloadClass()); return event; } @Getter private static class EventRaw { private Long eventId; private String type; private Object payload; } }
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
SnowFlake
현재 댓글 무한 depth까지 수강했는데 snowFlake를 통해 Id값을 넣고 있는데 해당 부분을 service단에서 entity.create를 통해 구현하는 점에 대해 궁금증이 생겼습니다.이런 방식으로 계속 진행이 되면 보일러 플레이트방식이 되는것 같은 느낌이 들어서 차라리 커스텀 IdentifierGenerator를 통해 Entity안에서 값을 넣어주면 어떨까하고 생각을 해보게 되었습니다. 이렇게 진행하면 기존 service를 통해 진행하는 것보다 비효율적일까요?? 두개의 차이점이 궁금합니다
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
physical cache miss 시, collapse 동작 안하는 상황에 대해
레디스에 physical 하게 남아있지 않은 캐시에 대해선 request collpsing이 동작하지 않습니다. 이를 해결하기 위한 방법이 따로 있을까요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
안녕하세요, MessageRelay 관련해 궁금한 사항 생겨서 문의드립니다.
안녕하세요, 강사님늘 강의 잘 듣고 있습니다. 감사합니다.강의를 보다보니, 아래 프로세스가 발생한다면 동시성 이슈가 발생할 수 있을 것 같기도 한데요,혹시 관련하여 의견 여쭙고자 글 남깁니다감사합니다.
-
해결됨커머스 서비스로 배우는 NestJS 실전 개발 (w. Prisma, Docker, Redis, Kafka)
dto 관련 질문있습니다.
order.service 계층에서import { CreateRequestOrderDto } from 'src/order/presentation/dto/create.order.dto';presentation 계층의 dto를 참조하는데 이유가 있을까요??궁금해서 질문드립니다.