묻고 답해요
150만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
순위 정보를
불러오고 있어요
-
미해결FastAPI 찍어먹기 (FastAPI + React.js + AWS LightSail)
schema 클래스의 model_config 속성 관련 질문
안녕하세요! fastapi 입문으로 강사님 강의를 듣게되어 영광입니다. 강의 내용 따라 진행은 잘되고 있으나 강사님이 구현하신 class SubCategoryCreate(BaseModel): model_config = ConfigDict(from_attributes=True) subcategory_name: str category_id: int model_config = { "json_schema_extra": { "example": { "subcategory_id": 1, "subcategory_name": "수도", "category_id": 1 } }이렇게 구현을 하셨는데 model_config 변수가 두번사용 되어 마지막에 사용된 model_config 값이 첫번째 ConfigDict(from_attributes=True) 값을 덮어 씌워 첫번째로 사용된 model_config 변수의 역할이 없어지는것이 아닌가 하여 의문이 들어 왜 같은 변수명으로 덮어 씌우는지 궁금하여 질문 드립니다.
-
미해결[리뉴얼] 처음하는 SQL과 데이터베이스(MySQL) 부트캠프 [입문부터 활용까지]
섹션4 화면 자체가 안나와요
다른 영상은 괜찮은데 섹션4는 목소리만 나옵니다. 화면은 블랙화면으로 계속 고정이네요. 다른 섹션은 안그런데... 저만 그런건가요??
-
미해결스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
각 사용자는 게시글 1개당 1번 조회수 증가'일 경우 설계 질문 드립니다.
안녕하세요 쿠케님!강의에 나온 10분의 TTL이 없을시 조회수 증가 관련되서 질문드립니다!' 현 상황에서 게시글 접근시 마다 조회수 증가 API를 호출하고 있습니다.테이블 정보는 다음과 같습니다. Board 테이블 id(auto-increment) readCountBoardRead 테이블 id(auto-increment) boardId userId 게시글 상세 접근시 마다 호출하는 '조회수 증가 API'의 흐름은 다음과 같습니다.1. 해당 게시글을 유저가 조회 했는지 검증 (BoardRead 테이블에서 검증)1-1. 조회 한 이력이 있으면 return;2. Board 비관적 락 조회3. Board 테이블 readCount 업데이트4. BoardRead 테이블 insert Board의 readCount는 게시글 조회수를 나타내고, BoardRead는'각 사용자는 게시글 1개당 1번 조회수 증가'를 검증하기 위한 용도 입니다.'각 사용자는 게시글 1개당 1번 조회수 증가' 정책을 반드시 가져가야 한다면BoardRead 테이블에 있는 데이터도 레디스로 옮겨야 할까요?그런데 조회수 데이터는 계속해서 쌓일테고 비즈니스에 중요하지 않은 데이터가 레디스 메모리만차지하는 느낌이 들어서 꺼려지더 라구욤.. 강의 내용대로 TTL을 걸수 밖에 없는건가 고민도 듭니당..(레디스는 클러스터 환경으로 사용하고 있습니다.) 기능은 그대로 유지하되 비관적 락을 뺄 수 있는 방법이 있을까요? ※ 번외로 트래픽 바로 몰리니까 비관적 락 로직 때문인지 잠금 이슈 나서 디비 바로 터졌버렸네요 하하하 ㅠ비관적 락을 선호하지 않는 이유를 체감해버렸다.. ※ 뇌 + GPT 갈구니까 아래와 같은 여러 결론이 나왔습니다.해결책11. Redis SET 자료구조로 중복 체크, 최초 조회면 TTL 걸어줌2. 분산락(Redis)을 걸고, Board 테이블 readCount 업데이트 및 BoardRead Insert 수행3. 락 해제해결책21. Redis SET 자료구조로 중복 체크, 최초 조회면 TTL 걸어줌2. 비동기로 Board 테이블 readCount 업데이트(낙관적 락 적용) 및 BoardRead Insert 수행작성하면서 문득 'readCount를 정규화 할까?' 했는데 스케이링 넘 클것 같네요..이유는 테이블 설계를 JPA의 상속을 활용하는 방안으로 했기 때문에 readCount 필드가 '게시판'이란 추상 클래스에 위치해 있습니다.주저리 주저리 적어봤는데 머릿속에 혼란이 오네요 ㅠㅠ자기전 마지막 생각레디스의 incr를 이용해 조회수 관리, 조회수 데이터는 mysql에 주기적으로 백업
-
미해결[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
무료/프리미엄 동시 소유 시 질문
다른 분들의 질문에 답변을 달아주신 내용을 보니까 사용자 계정에 등록된 도메인의 0번째에서 type 값을 사용하여 무료/프리미엄을 구분하는 답변을 예시로 준 것을 봤습니다.프로젝트 환경에서 하나의 사용자가 같은 도메인에 무료/프리미엄 비밀 키를 중복하여 발급 받을 수도 있고 비밀 키를 다수 발급받을 수도 있는데요 이 경우 도메인은 여러개가 조회될 것인데 이런 경우에는 어떻게 처리를 해야하나요?헤더에 비밀 키를 담아서 요청을 보내서 어떠한 비밀 키를 통한 요청인지를 특정하는 방법도 생각해봤습니다.서버 비밀 키는 어차피 클라이언트에게는 노출될 우려가 없고 클라이언트 용 비밀 키는 어차피 클라이언트에게 노출될 것을 염두해둔 비밀 키니까 노출이 되어도 크게 문제가 없을거라 생각했었습니다.하지만 이게 보안에 문제가 되는 방법은 아닌지 모르겠어서 검색을 좀 해봤지만 쉽게 판단이 서질 않습니다.아니면 일단 현재 수준에서는 보안 측면까지 이렇게 고민하기 보다 서버/클라이언트용과 무료/프리미엄용을 분리함에 의의를 두고 이런 강의를 진행하는 것이 옳은걸까요?ㅜㅜ[제로초 강좌 질문 필독 사항입니다]질문에는 여러분에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요.4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
-
해결됨[개발부터 수익화까지] AI로 코드 한 줄 짜지 않고 만드는 IT 올인원 실전 프로젝트!
ai툴 유료버전사용?
혹시 커서와 챗지피티 전부 유료버전 끊어서 사용하고 계신가요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
댓글 수 구현에서 동시성 문제 해결 질문드립니다
학습 관련 질문을 최대한 상세히 남겨주세요!고민 과정도 같이 나열해주셔도 좋습니다.먼저 유사한 질문이 있었는지 검색해보세요.인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.안녕하세요 댓글 수 구현 강의를 해보다가 동시성 문제를 해결해보고 싶어서 비관적 락 for update를 사용하는 방법으로 한번 코드를 짜보고 테스트를 해보고 있습니다.코드는 아래처럼 짜보았습니다@Table(name = "article_comment_count") @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @ToString public class ArticleCommentCount { @Id private Long articleId; private Long commentCount; public static ArticleCommentCount init(Long articleId, Long commentCount) { ArticleCommentCount articleCommentCount = new ArticleCommentCount(); articleCommentCount.articleId = articleId; articleCommentCount.commentCount = commentCount; return articleCommentCount; } public void increase() { this.commentCount++; } public void decrease() { this.commentCount--; } }public interface ArticleCommentCountRepository extends JpaRepository<ArticleCommentCount, Long> { @Lock(LockModeType.PESSIMISTIC_WRITE) Optional<ArticleCommentCount> findLockedByArticleId(Long articleId); }@Service @RequiredArgsConstructor public class CommentService { private final CommentRepository commentRepository; private final Snowflake snowflake = new Snowflake(); private final ArticleCommentCountRepository articleCommentCountRepository; @Transactional public CommentResponse create(CommentCreateRequest request) { Comment parent = findParent(request); Comment comment = commentRepository.save( Comment.create( snowflake.nextId(), request.getContent(), parent == null ? null : parent.getCommentId(), request.getArticleId(), request.getWriterId() ) ); ArticleCommentCount articleCommentCount = articleCommentCountRepository.findLockedByArticleId(request.getArticleId()) .orElseGet(() -> { ArticleCommentCount newCount = ArticleCommentCount.init(request.getArticleId(), 0L); articleCommentCountRepository.save(newCount); return newCount; }); articleCommentCount.increase(); articleCommentCountRepository.save(articleCommentCount); return CommentResponse.from(comment); } private Comment findParent(CommentCreateRequest request) { Long parentCommentId = request.getParentCommentId(); if (parentCommentId == null) { return null; } return commentRepository.findById(parentCommentId) .filter(not(Comment::getDeleted)) .filter(Comment::isRoot) .orElseThrow(); } public CommentResponse read(Long commentId) { return CommentResponse.from(commentRepository.findById(commentId).orElseThrow()); } @Transactional public void delete(Long commentId) { commentRepository.findById(commentId) .filter(not(Comment::getDeleted)) .ifPresent(comment -> { if (hasChildren(comment)) { comment.delete(); } else { delete(comment); } }); } private boolean hasChildren(Comment comment) { return commentRepository.countBy(comment.getArticleId(), comment.getCommentId(), 2L) == 2; } private void delete(Comment comment) { commentRepository.delete(comment); articleCommentCountRepository.findLockedByArticleId(comment.getArticleId()) .ifPresent(articleCommentCount -> { articleCommentCount.decrease(); articleCommentCountRepository.save(articleCommentCount); }); if(!comment.isRoot()) { commentRepository.findById(comment.getParentCommentId()) .filter(Comment::getDeleted) .filter(not(this::hasChildren)) .ifPresent(this::delete); } } public CommentPageResponse readAll(Long articleId, Long page, Long pageSize) { return CommentPageResponse.of( commentRepository.findAll(articleId, (page - 1) * pageSize, pageSize).stream() .map(CommentResponse::from) .toList(), commentRepository.count(articleId, PageLimitCalculator.calculatePageLimit(page, pageSize, 10L)) ); } // 무한 스크롤 public List<CommentResponse> readAll(Long articleId, Long lastParentCommentId, Long lastCommentId, Long limit) { List<Comment> comments = lastParentCommentId == null || lastCommentId == null ? commentRepository.findAllInfiniteScroll(articleId, limit) : commentRepository.findAllInfiniteScroll(articleId, lastParentCommentId, lastCommentId, limit); return comments.stream() .map(CommentResponse::from) .toList(); } public Long count(Long boardId) { return articleCommentCountRepository.findById(boardId) .map(ArticleCommentCount::getCommentCount) .orElse(0L); } }@Test void concurrencyCountTest() throws InterruptedException { Long articleId = 24L; int threadCount = 10; ExecutorService executorService = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); for(int i = 0; i < threadCount; i++) { final Long writerId = (long) (1000 + i); executorService.execute(() -> { try { createComment(new CommentCreateRequest(articleId, "concurrency test", null, writerId)); } catch (Exception e) { System.err.println("Exception in thread: " + Thread.currentThread().getName() + " -> " + e.getMessage()); } finally { latch.countDown(); } }); } latch.await(); Long commentCount = restClient.get() .uri("/v1/comments/articles/{articleId}/count", articleId) .retrieve() .body(Long.class); System.out.println("최종 commentCount = " + commentCount); assertThat(commentCount).isEqualTo(threadCount); }그런데 이렇게 했을때맨 처음 실행을 하면 1개의 데이터만 삽입되고 나머지 9개는 소실이됩니다그리고 한번더 실행하면 11개의 데이터가 저장되는데 맨처음 저장된 1개의 데이터 + 10개의 스레드가 저장한 10개의 데이터가 되어 11개가 됩니다.여기서 문제가 article_comment_count 테이블에 데이터가 아예 없을때 10개의 스레드가 동시에 insert문을 날리려고해서 Duplicate entry '24' for key 'article_comment_count.PRIMARY' 이런 문제가 나오지 않나 생각이 듭니다만.. create 메서드에 @Transactional(isolation = Isolation.READ_COMMITTED)로 격리 수준을 높여봤지만 여전히 문제가 해결되지 않습니다.혹시 제가 잘못 이해한 부분이 있을까요? 그리고 동시성 문제를 해결하려면 어떻게 해야할까요? gpt에 물어보거나 구글링해서 찾아봐도 해결이 되지 않아서 질문드립니다!
-
미해결[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
비주얼 스튜디오 코드로 계속 진행해도 괜찮을까요?
여태까지 비주얼 스튜디오 코드로 하시다가 웹스톤으로 바꾸셨던데혹시 비주얼 스튜디오 코드로 계속 진행해도 프로그램 실행에는 문제없을까요?
-
미해결[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
10강 cors에러 localhost:4000으로 접속했을때 에러
에러로 의심되는 코드 올릴게요.아래는 nodebird-api 의 routes/index 파일과 middlewares/index.js코드의 corsWhenDomainMaches 모듈인데 뭐가문제인지 모르겠습니다. cors모듈은 사용했는데 말이죠 module.exports = router; exports.corsWhenDomainMatches = async (req, res, next) => { const domain = await Domain.findOne({ where: { host: new URL(req.get('origin')).host }, }); if (domain) { cors({ origin: req.get('origin'), credentials: true, })(req, res, next); } else { next(); } }; const express = require('express'); const { verifyToken, apiLimiter, corsWhenDomainMatches } = require('../middlewares'); const { createToken, tokenTest, getMyPosts, getPostsByHashtag } = require('../controllers/v2'); const router = express.Router(); router.use(corsWhenDomainMatches); // POST /v2/token router.post('/token', apiLimiter, createToken); // POST /v2/test router.get('/test', apiLimiter, verifyToken, tokenTest); // GET /v2/posts/my router.get('/posts/my', apiLimiter, verifyToken, getMyPosts); // GET /v2/posts/hashtag/:title router.get('/posts/hashtag/:title', apiLimiter, verifyToken, getPostsByHashtag);
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
조회수를 RDB에만 저장하고 있는 서비스에서 Redis 도입 관련해서 질문입니다.
안녕하세요. 쿠케님강의 너무나 잘 보고 있습니다. 쿠케님 질의응답 게시글 정독하고 있는데 1:1 멘토링 부럽지 않을만큼의 고퀄리티 답변 언제나 감사드립니다. (갬동갬동) 현재 회사에서 조회수를 RDB에만 저장하고 있습니다.이를 강의 내용처럼 'Redis 이전 & MySql 백업'을 적용 하고 싶은데 아래 흐름대로 적용 하면 될까요?1⃣ (개발) redis 저장 및 조회, mysql 백업 코드 작성2⃣ (개발) 기존 MySQL 데이터를 Redis에 저장하는 마이그레이션 코드 작성=> 이렇게 일회성 마이그레이션 같은 경우 테스트 코드로 한번만 돌리는데 쿠케님은 어떤 방식으로 하시나요?3⃣ (배포 전) 1번 코드 배포 직전에 2번 로직 실행4⃣ (배포) 1번 코드 배포 위 방식에서 발생할 수 있는 문제점1. 3번(마이그레이션)과 4번(배포) 사이에 조회수가 누락될 가능성- 마이그레이션 실행 후 MySQL에는 새로운 조회수가 계속 업데이트되지만, Redis는 아직 트래픽을 받지 않음.- 즉, 마이그레이션 실행 이후 MySQL에 새로 기록된 조회수는 Redis에 반영되지 않음 → 데이터 불일치 발생 가능.2. 처음 Redis로 전환할 때, Redis에 캐싱되지 않은 일부 조회수가 MySQL에 계속 쿼리될 가능성이 있음.- 처음 Redis로 전환할 때, Redis에 캐싱되지 않은 일부 조회수가 MySQL에 계속 쿼리될 가능성이 있음.- 특정 조회수가 빠르게 증가하면 Hot Key 이슈 발생 가능. 조회수 누락을 최소화 하는 방법1⃣ (개발) redis 저장 및 조회, mysql 백업 코드 작성2⃣ (개발) 기존 MySQL 데이터를 Redis에 저장하는 마이그레이션 코드 작성 및 실행3⃣ (1번 코드 배포 전) 더블 라이트(Double Write) 모드 개발하여 운영에 적용4⃣ (배포) 기존 MySQL 기반 조회수 코드 제거, Redis 기반으로 전환5⃣ (배포 후) MySQL 백업 로직 실행 및 기존 MySQL 조회 로직 완전히 제거제가 혹여나 놓친게 있거나 더 좋은 방법이 있으면 천천히 답변 부탁드립니닷! 출처: 내 뇌 + GPT 센세
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
id관련
안녕하세요. 강의 잘 듣고 있습니다.제가 원래 질문이 많은데 개념을 이해가 잘 되도록 쉽게 설명해 주셔서 질문 드릴게 별로 없네요. 보통 api path에 id를 추가하시는거 같은데요.현업에서도 auto_increment나, snowflake id같은 db에서 쓰는 id를 그대로 넣고 사용하시나요?아니면 prefix등을 추가하여 조금 더 가공을 한다든지 하시나요?숫자만 들어가니 좀 밋밋해 보이기도 하고 알아보기도 힘들거 같기도 해서요.
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
테스트 작성 관련 간단한 질문입니다.
안녕하세요. 강의 잘 듣고 있습니다.테스트 작성하실때 request와 response를 import 안하고 따로 inner class로 만드시는 특별한 이유가 있을까요?궁금하네요.
-
해결됨Spring Boot를 활용하여 채팅 플랫폼 만들어보기
ChatListResponse에 @Valid을 붙인 이유
안녕하세요! 강의 잘듣고 있습니다.다름이 아니라ChatControllerV1에@GetMapping("/chat-list") public ChatListResponse chatList( @RequestParam("name") @Valid String to, @RequestParam("from") @Valid String from ) { return chatServiceV1.chatList(from, to); }@Valid를 붙이셨는데 제가 알기론 @Valid 가 붙는 쪽은 컨트롤러단에서 DTO또는 Entity 매개변수 앞에 붙어서 유효성 검사를 하는걸로 알고있습니다!제 지식이 맞다면 왜 String 에 앞단에 넣은 이유가 궁금합니다..!아니면 이것도 추후 DTO 또는 request, response 객체들이 들어갈 수 있기 때문에 들어가나요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
아웃박스 패턴과 로그 테일링 기법의 선택 기준에 대해서
안녕하세요 쿠케님 강의 잘 듣고 있습니다.아웃박스 패턴과 로그 테일링 기법은 물론 애플리케이션의 특징마다 다르겠지만로그 테일링 기법이 선호되는 경우는 어떤게 있을까요? LLM 챗봇들과 열심히 논의해본 결과 둘은 실시간성과 구현의 단순성에서 가장 큰 차이로 느꼈는데요. 실제로도 두 가지가 핵심적인 기준 역할을 하는지가 궁금하고 이외에 제가 찾지 못한 다른 것이 있다면 무엇이 있을까요?
-
미해결입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
Admin 뷰 템플릿 유료화 여부 및 변경 사항 문의드립니다.
안녕하세요.항상 좋은 강의 제공해 주셔서 감사합니다.현재 Admin 뷰 템플릿을 적용하는 강의를 듣고 있습니다.강의에서 안내해 주신 방법대로 뷰 템플릿을 다운로드받기 위해 해당 페이지에 접속했는데, 아래 이미지와 같이 표시되어 있어 질문드립니다.혹시 최근 업데이트로 인해 Admin 뷰 템플릿이 유료화된 것인지, 또는 다른 변경 사항이 있는지 확인해 주실 수 있을까요?개인적으로는 2025년 3월 11일 업데이트 이후 유료화된 것으로 보이는데, 정확한 내용이 궁금합니다.답변 부탁드립니다.감사합니다. [URL]: https://bootstrapmade.com/nice-admin-bootstrap-admin-html-template
-
미해결[리뉴얼] 처음하는 SQL과 데이터베이스(MySQL) 부트캠프 [입문부터 활용까지]
강의 화면이 안 보이고, 목소리밖에 안나와요 ;;
- 강의 영상에 대한 질문이 있으시면, 상세히 문의를 작성해주시면, 주말/휴일 제외, 2~3일 내에 답변드립니다 (이외의 문의는 평생 강의이므로 양해를 부탁드립니다.)- 강의 답변이 도움이 안되셨다면, dream@fun-coding.org 로 메일 주시면 재검토하겠습니다. - 괜찮으시면 질문전에 챗GPT 와 구글 검색을 꼭 활용해보세요~- 잠깐! 인프런 서비스 운영(다운로드 방법포함) 관련 문의는 1:1 문의하기를 이용해주세요.
-
해결됨Spring Boot를 활용하여 채팅 플랫폼 만들어보기
checkTokenForRefresh에 대해 궁금한점
안녕하세요! 항상 강의 잘 듣고 있습니다!그런데 강의를 듣다가 checkTokenForRefresh을 해주는 메서드를 만드셨는데 제가 이해한것이 맞는지 궁금해서 질문드립니다!이 메서드에서 만료가 되면 TokenExpiredException이 일어나면 토큰이 만료되서 decode 을 하시는데 위에 메서드들 처럼 createToken을 안하는 이유가 만료된 토큰의 정보 바탕으로 바로 똑같이 바로 만들어지는 건가요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
CommentApiTest에서 readAll() 메서드에서의 SQLException
안녕하세요 쿠케님 ! CommentApiTest에서 readAll() 메서드의 body(CommentPageResponse.class); 부분에서 SQLException이 발생하는 것을 확인했습니다. 하지만 테이블 컬럼을 확인해보니, Comment 테이블에 content 컬럼이 분명히 존재하는걸 확인할 수 있었습니다.그리고 실제로 readAll() 메서드가 사용되는 쿼리에서는 content 컬럼이 사용되지도 않는데, 어째서 content 컬럼을 찾을 수 없다고 하는지 모르겠습니다. findAll()의 쿼리가 잘못됐나 해서 쿼리문을 직접 실행을 해도 잘 돌아가는걸 확인할 수 있었습니다. 코드도 첨부하겠습니다.
-
미해결입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기
테스트 코드 작성 중 AssertionError 발생
테스트 코드 작성에서 오류가 뜨는데 어떤 오륜지 잘 모르겠습니다.beforeAll() 함수에서는 Assertions 임포트 잘 되는거같은데 테스트 시작이 안되네요..
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
comment에서 findParent 질문드립니다.
학습 관련 질문을 최대한 상세히 남겨주세요!고민 과정도 같이 나열해주셔도 좋습니다.먼저 유사한 질문이 있었는지 검색해보세요.인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. findParent 메서드에서 return문을 보면 getDeleted로 부모 댓글이 삭제됐는지 여부를 확인하는 코드가 있는데, 부모가 삭제됐을때를 체크하는 이유가 무엇인가요? 부모 댓글이 삭제됐을 때, 더이상 하위 댓글을 달 수 없도록 하기 위함인가요? 하위 댓글이 남아있는 이상 부모 댓글은 "삭제됐습니다"와 같이 표시만 할 뿐이고 db에서 삭제되지는 않더라도 deleted=true로 바뀌어 있을텐데 만약 부모 댓글이 삭제됐을때 하위 댓글을 추가로 달 수 없는게 아니라, 계속해서 하위 댓글을 달 수 있다면 부모 댓글이 삭제된 상태라 하더라도 create 메서드를 실행할때 부모 댓글의 id를 넣어도 문제 없지 않은가라는 의문이 들어서요 create 메서드를 보면 parent가 null이라면 부모댓글이니까 null을 넣고 하위 댓글이라면 findparent에서 찾은 부모 댓글의 id를 넣어주는 코드인데, 부모 댓글이 deleted=true로 바꼈다 할지라도 DB 자체에서 완전히 삭제되지 않은 이상 즉, 하위 댓글이 여전히 남아있는 상황이라면 deleted=true로 바뀐 부모 댓글의 id를 넣어줘도 되지 않나?라는 생각이 들어서 질문드립니다!
-
미해결[켠김에 출시까지] 유니티 캐주얼 모바일 MMORPG (M2)
aws 와 microsoft azure 중 aws 선택하신 이유가 궁금합니다.
클라우드 서비스로 aws와 microsoft azure 둘중 비쥬얼 스튜디오랑 호환이 잘되있는 microsoft azure도 괜찮은 선택지일수 있을거같은데, 루키스님이 aws 고르신 이유가 궁금합니다. !
주간 인기글
순위 정보를
불러오고 있어요