김영한
@yh
수강생
583,964
수강평
40,621
강의 평점
5.0
교육자
전: 우아한형제들 기술이사, 카카오, SK플래닛
진짜 실무에 필요한 제대로 된 개발자가 될 수 있도록, 교육하는 것이 저의 목표입니다.
저의 개발 인생 이야기
EO 인터뷰 영상
개발바닥 - 시골 청년 개발왕 되다
취업과 이직에 대한 고민 해결
강의
로드맵
전체 4수강평
- 김영한의 자바 입문 - 코드로 시작하는 자바 첫걸음
- 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
- 김영한의 실전 데이터베이스 입문 - 모든 IT인을 위한 SQL 첫걸음(SQL부터 차근차근)
- 김영한의 실전 데이터베이스 입문 - 모든 IT인을 위한 SQL 첫걸음(SQL부터 차근차근)
게시글
질문&답변
공통 코드에서 Redis Pub/Sub은 최근 실무에서 쓰이진 않나요?
안녕하세요. kekim님AI 인턴이 잘 답변해주었는데요. 조금 더 보충하자면이 부분은 실무에서 겪고 있는 상황에 따라 다릅니다.비즈니스 로직상 정확한 갱신이 너무 중요하다면 Redis Pub/Sub은 고민하신 것 처럼 리스크가 있습니다.다만 현재 팀에서 Redis를 잘 사용하고 있고, 비즈니스 로직상 만약에 갱신에 일부 실패해도 허용 가능한 범위의 리스크라면 Redis Pub/Sub을 사용해도 괜찮습니다.감사합니다.
- 0
- 2
- 53
질문&답변
캐시무효화시 그냥 no-store만 넣어되지 않나요?
안녕하세요. 캐내기님다음을 참고해주세요 🙂https://inf.run/ucXRb감사합니다.
- 0
- 2
- 29
질문&답변
제네릭 타입 매개변수 제한과 관련한 문의입니다.
안녕하세요. 서규환님AnimalHospitalV3처럼 상위 타입을 타입 인자로 직접 지정하면, Dog과 Cat이 섞여 들어와도 컴파일러가 잡아주지 못하는 것이 맞습니다.결론부터 말씀드리면, 이것은 제네릭의 한계가 아니라 사용하는 쪽의 책임입니다.제네릭의 설계 의도를 먼저 생각해보면제네릭은 "사용하는 시점에 타입을 구체적으로 결정하라"는 것이 핵심입니다. 즉, 다음과 같이 사용하는 것이 올바른 사용법입니다.AnimalHospitalV3 dogHospital = new AnimalHospitalV3(); AnimalHospitalV3 catHospital = new AnimalHospitalV3();AnimalHospitalV3로 선언하는 것은, 개발자가 스스로 "나는 모든 Animal을 다 받겠다"고 선언한 것입니다. 이것은 제네릭을 쓰면서 일부러 다형성 방식으로 돌아간 것이기 때문에, 그에 따르는 책임(캐스팅 등)도 개발자가 지게 됩니다. 그러면 실무에서 실수를 줄이는 방법은 무엇이 있을까요?1. 코드 리뷰와 컨벤션으로 방지실무에서 가장 현실적인 방법입니다. "타입 인자로 상위 타입을 직접 지정하지 말 것"이라는 팀 컨벤션을 정하는 것입니다. 사실 대부분의 경우 로 지정할 이유가 없습니다. 구체적인 타입으로 지정하는 것이 제네릭의 올바른 활용법이니까요.2. 와일드카드를 활용한 읽기 전용 제한만약 여러 Animal 타입을 함께 다뤄야 하는 상황이라면, 와일드카드를 활용할 수 있습니다. 이 부분은 뒤에서 학습하게 됩니다.// 데이터를 꺼내서 읽기만 하는 경우 void printAnimal(AnimalHospitalV3 hospital) { // hospital.set(new Dog(...)); // 컴파일 오류! 넣을 수 없음 Animal animal = hospital.get(); // 꺼내는 것만 가능 }이렇게 하면 꺼내서 읽는 것은 가능하지만, 잘못된 타입을 넣는 실수는 컴파일 시점에 막을 수 있습니다.3. 근본적인 관점자바의 타입 시스템에서 AnimalHospitalV3이라는 선언 자체를 금지하는 방법은 없습니다. 질문에서 언급하신 것처럼 Animal을 추상 클래스나 인터페이스로 만들어도, 타입 인자로 지정하는 것까지 막지는 못합니다.하지만 이것은 Object obj = new Dog()와 본질적으로 같은 문제입니다. 자바에서 상위 타입으로 참조하는 것 자체를 금지할 수는 없고, 그렇게 하는 순간 발생하는 책임은 개발자에게 있는 것입니다.정리하면제네릭의 가치는 , 처럼 구체적인 타입을 지정했을 때 빛을 발합니다. 로 지정하는 것은 제네릭의 장점을 스스로 포기하는 것이므로, "구체적인 타입을 지정해서 사용하는 것이 원칙"이라고 이해하시면 됩니다. 실무에서는 코드 리뷰를 통해 이런 부분을 잡아내고, 필요한 경우 와일드카드를 활용하는 것이 가장 현실적인 방법입니다. 하지만 약간의 대안도 있습니다. 1. 구체적인 타입 인자 사용 원칙 (가장 기본적이고 권장되는 방법)제네릭의 본래 도입 목적은 사용할 타입을 명확히 지정하여 컴파일 시점에 오류를 잡는 것입니다. 따라서 객체를 생성할 때 AnimalHospitalV3 대신, AnimalHospitalV3, AnimalHospitalV3과 같이 명확한 하위 타입을 지정해서 생성하는 것을 기본 원칙으로 삼아야 합니다. 이렇게 구체 타입을 명시하면, 개 병원에 고양이를 넣으려고 할 때 컴파일러가 완벽하게 오류를 잡아냅니다.2. 정적 팩토리 메서드를 활용한 객체 생성 제한클라이언트 개발자가 실수로 을 넣어서 인스턴스를 생성하는 것을 아예 막고 싶다면, 생성자를 막고 정적 팩토리 메서드를 제공하는 방식을 사용할 수 있습니다.public class AnimalHospitalV3 { // 1. 외부에서 직접 생성을 막음 private AnimalHospitalV3() {} // 2. 정적 팩토리 메서드로 특정 구체 타입만 생성하도록 유도 public static AnimalHospitalV3 createDogHospital() { return new AnimalHospitalV3(); } public static AnimalHospitalV3 createCatHospital() { return new AnimalHospitalV3(); } // ... } 이렇게 설계하면 개발자가 무심코 new AnimalHospitalV3()을 호출하는 것을 구조적으로 차단하고, 허용된 구체 타입의 병원만 생성하도록 강제할 수 있습니다. 감사합니다.
- 0
- 3
- 35
질문&답변
AspectV1 예제를 @Configuration 수동 등록으로도 가능한가요?
안녕하세요. 이상우님수동 빈 등록을 사용해도 정상적으로 작동해야 합니다.확인을 하려면 전체 코드를 봐야할 것 같아요.실제 동작하는 전체 프로젝트를ZIP파일로 압축해서 구글 드라이브로 공유해서 링크를 남겨주세요.구글 드라이브 업로드 방법은 다음을 참고해주세요.https://bit.ly/3fX6ygx주의: 업로드시 링크에 있는 권한 문제 꼭 확인해주세요추가로 다음 내용도 코멘트 부탁드립니다.1. 문제 영역을 실행할 수 있는 방법2. 문제가 어떻게 나타나는지에 대한 상세한 설명 (오류 화면, 오류 로그 포함)링크: 공식 서포터즈링크: 자주하는 질문감사합니다.
- 0
- 2
- 47
질문&답변
모니터와 synchronized, ReentrantLock, 원자적연산 CAS관련해서 추가적으로 더 깊게 공부했는데 제가 이해한 것이 맞나요??
안녕하세요. 정재익님다른 부분은 맞는데요. 다음 부분은 정정이 필요합니다.synchronized는 자바가 구현한 모니터인데 조건변수의 추가가 불가능해서 생산자 소비자 문제를 해결할 수 없습니다.이 부분은 반은 맞고 반은 틀렸습니다. synchronized 역시 자바의 모든 객체가 기본적으로 가지고 있는 모니터 락과 '스레드 대기 집합(wait set)'이라는 조건 변수를 사용합니다. 따라서 Object.wait()와 Object.notify()를 사용하면 생산자 소비자 문제를 해결할 수는 있습니다.진짜 문제는 '조건 변수의 추가가 불가능하다'는 점입니다. synchronized는 내부적으로 단 1개의 스레드 대기 집합만 가집니다. 따라서 생산자 스레드와 소비자 스레드가 같은 대기 공간에 섞여서 대기하게 됩니다. 이로 인해 생산자가 데이터를 만든 후 소비자를 깨우고 싶은데 실수로 대기 중인 다른 생산자를 깨우거나(비효율), 반대로 소비자가 다른 소비자를 깨우는 비효율적인 문제가 발생합니다.(결과적으로 로직은 동작하지만, 헛도는 스레드가 생겨 비효율적입니다.)감사합니다 🙂
- 0
- 1
- 56
질문&답변
comment 채번을 사용해야 하는 이유에 대한 설명이 필요합니다.
안녕하세요. 변현진님 🙂AUTO_INCREMENT는 테이블 전체를 기준으로 1씩 순차적으로 증가하는 고유한 값을 만들어냅니다. 하지만 식별 관계인 (board_id, comment_no) 구조에서 우리가 기대하는 comment_no는 다음과 같이 동작해야 합니다.1번 게시글의 댓글: (1, 1), (1, 2), (1, 3)2번 게시글의 댓글: (2, 1), (2, 2)즉, board_id가 달라지면 comment_no는 다시 1부터 시작해야 합니다.따라서 AUTO_INCREMENT를 사용할 수 없습니다.감사합니다.
- 0
- 3
- 71
질문&답변
추후 강의 질문있습니다
치즈초코우유님 안녕하세요.말씀하신 애플리케이션 아키텍처 부분이 제가 가장 좋아하고 또 가장 자신있는 분야인데요.저도 어서 그런 강의를 하고 싶은데, 그런데 이런 부분을 정말 제대로 설명하려면 듣는 분들께서 우선 수 많은 기초들이 다 정리가 되어야 겠더라구요.예를 들어서 자바, 데이터베이스, 스프링, JPA 등등 모든 기반이 있어야 진짜 깊이있는 애플리케이션 아키텍처에 대한 이해가 가능하다 생각합니다.지금은 데이터베이스 로드맵을 완성하는데 집중하고 있지만, 이후 강의 진행 방향중 하나의 큰 축으로 애플리케이션 아키텍처가 들어가 있다고 보시면 됩니다 🙂감사합니다.
- 0
- 2
- 96
질문&답변
자바 기본편 - .(dot)에 관한 질문입니다!
안녕하세요. 7580750님생각하신 내용이 맞습니다^^!감사합니다.
- 0
- 1
- 64
질문&답변
setVlaue질문
안녕하세요. 여비님질문하신 부분은 강의에서 가변 객체(Mutable)와 불변 객체(Immutable)를 비교해서 설명하기 위해 서로 다른 두 개의 클래스를 번갈아 사용하고 있기 때문에 발생한 혼란입니다.결론부터 말씀드리면, setValue()를 다시 만든 것이 아니라 가변 객체인 Address 클래스를 사용한 예제를 먼저 보여주었기 때문입니다,.뒤에서 ImmutableAddress를 보여드리는데요. ImmutableAddress는 불변이고, Address는 가변입니다.감사합니다.
- 0
- 3
- 65
질문&답변
실무 통계 질문(고민) 드립니다..!
안녕하세요. 감사합니두 ~님상황에 따라 다른 통계를 제공해야 해서 고민이 많으시겠네요. 1. 비즈니스 상황에 따라 다르겠지만, 최대한 많은 부분에서 중복과 코딩의 양을 줄이기 위해서 차트에 대한 메타데이터 테이블"을 별도로 설계하여 매핑하는 방식을 권장합니다.메타데이터의 정보만으로 많은 것을 자동화 하실 수 있을거에요.추가로 과거 RDB를 시도하셨다가 제각각인 Raw Data 구조 때문에 포기하신 것은 어쩌면 당연한 결정입니다. 차트마다 요구하는 데이터 구조가 완전히 다르기 때문입니다.이를 해결하는 표준적인 방법은 정규화된 컬럼을 포기하고 "고정 메타 컬럼 + 유연한 데이터 컬럼(JSON)"의 조합으로 가는 방법이 있습니다. (강의 마지막에 관계형 DB에서 JSON 방법 설명) 2. 이미 아실 것 같기도 한데, 데이터 파이프라인 (ETL)의 명확한 역할 분리하는 것을 권장합니다.매일 새벽 3시에 도는 배치를 논리적으로 3단계로 분리하면 관리와 재처리가 훨씬 쉬워집니다.1. Extract & Load (수집): 30만 번의 외부 API를 최대한 빠르게 긁어와서 원본 그대로 MongoDB(Raw)에 덤프합니다.2. Transform (가공): 메타데이터 테이블의 규칙을 읽어, MongoDB의 Raw Data를 차트 모양에 맞게 집계한 뒤 Data Mart (고정 메타 컬럼 + JSON 테이블)에 영구 저장합니다.3. Cache Warming (캐시 적재): Data Mart의 결과를 Redis에 미리 올려둡니다.감사합니다.
- 0
- 2
- 70









