묻고 답해요
130만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨스프링 핵심 원리 - 고급편
spring-boot-starter-jdbc dependency 추가 시 에러
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]커리큘럼 완강 후 샘플 프로젝트 만들면서 공부 중에 질문이 생겼습니다. 빈 후처리기 적용하는 과정 중에 추가로 jdbc dependency를 넣었습니다. build.gradle 파일에implementation 'org.springframework.boot:spring-boot-starter-jdbc'위와 같이 추가하고 실행하면create proxy: target class=class hello.proxy.app.v3.OrderRepositoryV3$$SpringCGLIB$$0 proxy class=class jdk.proxy2.$Proxy63 The bean 'orderRepositoryV3' could not be injected because it is a JDK dynamic proxy OrderRepositoryV3의 target 클래스가 SpringCGLIB로 생성되서 오류가 나고JDBC dependency를 주석 처리하고 gradle 빌드 후 다시 실행해보면 잘 실행됩니다. jdbc dependency를 추가해서 오류가 발생하는 이유와 해결 방법이 궁금합니다.
-
해결됨C개발자를 위한 최소한의 C++
일반 참조형과 const 참조형에 따라 컴파일 오류가 발생하거나 발생하지 않는 이유가 궁금합니다.
안녕하세요. 강의 잘 보고 있습니다.강의 내용 중 하나 이해 안되는 것이 있어 질문 남깁니다.// 위 코드 생략 TestData testFunc(TestData& rhs) { // 생략 } int main() { TestData result = testFunc( 10 ); // 생략 } testFunc의 매개변수로 10이 넘어갔기 때문에 묵시적으로 변환 생성자 TestData(int)가 호출된 것으로 보입니다.따라서 testFunc은 생성된 TestData를 참조로 받고 있는 것 같은데, 왜 위처럼 코드를 작성하면 오류가 발생하는지 이해가 되지 않습니다.// 위 코드 생략 TestData testFunc(const TestData& rhs) { // 생략 } int main() { TestData result = testFunc( 10 ); // 생략 } 위처럼 const 키워드를 붙였을 땐 오류가 안 발생하는데 이유가 무엇인가요?
-
해결됨C개발자를 위한 최소한의 C++
dll 파일과 name mangling 관련 질문드립니다.
안녕하세요. 강의 잘 보고 있습니다.강의를 보는데 갑자기 궁금한 것이 생겨서 질문 드립니다.exe 파일에서 필요할 때마다 dll을 참조하여 호출하는 것으로 이해하고 있습니다.dll엔 이미 함수 symbol이 정의되어 있겠죠.예를 들어 add(int a, int b) 함수를 dll로 만들었다고 가정하면 일반적인 c라면 함수 심볼이 _add라고 예상되지만, C++ 이라면 _add가 아니라 다른 형태의 심볼이 만들어질거라 생각이 됩니다.그런데 exe을 만드는 개발자 입장에서 생각해보면 무엇으로 만들어질지 모르는 심볼을 예측할 수 없으니 c처럼 _add와 같이 확실한 symbol을 선호할 것 같은데, 그러면 dll 파일을 c++로 만들 때 함수 앞에 extern "C" 키워드를 사용하나요?만약 1번 답이 맞다면 추가로 궁금한 것이 있습니다. 제가 알기론 C와 C++의 차이가 name mangling 뿐만 아니라 스택에 쌓이는 매개변수도 차이가 있다고 들었습니다. 그런데 C++로 만드는 exe 파일과 extern "C"로 선언한 dll을 같이 사용하면 문제가 생기지 않나요?부족한 것이 많아 질문에 오류가 있을 수 있습니다. 제가 잘못 알고 있는 부분은 피드백 주신다면 다시 공부하겠습니다.감사합니다.
-
해결됨C개발자를 위한 최소한의 C++
스크립트 오류 참조형(변하지 않는 포인터)
10:00 경 "근데 지금 값이 참조자인 ref에 들어있는 값하고 요놈하고 똑같죠. 네 그래서 여기에다 대고 그래서 간접지정해서 30 이러면은 F10키 눌러 보면 여기 데이터의 값이 바뀌는 걸 보실 수가 있죠? 네 데이터 출력하면 당연히 30이 나오겠네요. 해서 여러분이 지금까지 이만큼의 내용을 보셔서 아시겠지만 참조자라고 하는 것은 개념적인 표현이고 참조자의 실체는 포인터랑 똑같이 구현이 돼요. 그래서 기계어 수준으로 내려가서 쫓아가 보면은 결과적으로는 그렇다. 그래서 참고하시기 바랍니다. 아, 참조자 이런 거구나." 인데 이게 스크립트에서 이상하게 표시가 됩니다. 처리 부탁드릴게요. 고맙습니다!
-
해결됨C개발자를 위한 최소한의 C++
스크립트 오류
7:38초 경 텍스트가 그러면 그거랑 5랑 더해서 연산의 임시결과가 떨어졌겠죠 그게 15가 저장되어 있는 integer 인스턴스일텐데, 사라져야 될 운명이었으나 거기에다 이름을 부여한거죠. 인데 수정 제안할랬더니 길어서 짤리네요. 관리자분께서 스크립트 시간 자른 후 나눠서 바꿔주셔야 할 것 같아요.지난 시간 것도 유사한 게 있었어요. 통으로 날아가 있어서... 조치 부탁드립니다! 노트 필기하는 마음으로 스크립트 수정 중입니다. ㅎㅎ
-
미해결스프링 핵심 원리 - 고급편
다중 상속
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]인스턴트에 인터페이스가 있다면 JDK 동적 프록시를 사용하고 그렇기 때문에 "proxyFactory.getProxy()"의 반환값을 인터페이스로 타입 캐스팅할 수 있다고 알려주셨습니다. 그런데 만약이 객체가 인터페이스를 다중 상속하고 있다면 어떻게 동작하나요? 예를 들어서이런 예제가 있다고하면 InterfaceImpl는 A와 B 인터페이스를 모두 구현한 건데 JDK 동적 프록시는 InterfaceImpl이 A를 구현한건지 B를 구현한건지 어떻게 구분할까요?참고로 test 코드를 작성해보니 A와 B 둘다 타입 캐스팅이 되네요
-
미해결스프링 핵심 원리 - 고급편
http://localhost:8080/v0/request?itemId=hello 실행 시 에러
1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]예제 프로젝트 만들기에서 http://localhost:8080/v0/request?itemId=hello실행 시에 java.lang.IllegalArgumentException: Name for argument of type [java.lang.String] not specified, and parameter name information not found in class file either.이런 에러가 뜹니다.소스는 하도 안되서 다 복붙 해봤는데도 안됩니다.자바 17에 스프링부트 3.2.0 버전입니다.
-
미해결스프링 핵심 원리 - 고급편
프로젝트 생성시 자바 버전 관련
2023-11-25 기준으로 spring initializer 에 자바 11버전이 지원이 안되는건지 그림과 같이 21,17 버전만 있습니다 ㅜㅜ 혹시 왜그런건지...그리고 11버전으로느 ㄴ다운받을 수 없는건지 궁금합니다.
-
미해결코딩으로 학습하는 GoF의 디자인 패턴
싱글톤 패턴을 깨트리는 방법
싱글톤 패턴을 깨트리는 방법으로 Reflection, 직렬화/역직렬화 두 가지 방법을 설명해주셨고 굉장히 흥미롭게 들었습니다.다만 들으면서 싱글톤을 깨트리는 방법이 존재한다고 하더라도 저걸 고려하면서 코드를 짜야할까?라는 생각이 들었습니다.저는 아직 취준생이라 그런지 리플렉션이나, 직렬화/역직렬화 방식으로 객체를 생성해본 적이 없어서 이런 생각을 하는 거 같습니다. 실무에서 리플렉션을 이용해서 객체를 생성하는 경우가 종종 발생하나요? 굳이 왜 리플렉션을 이용해 객체를 생성하는 건지 어떨 때, 리플렉션을 사용해서 객체를 생성하는지 예시가 궁금합니다. 스프링을 쓰면서 직렬화/역직렬화가 이루어지는 것은 아래 정도의 케이스밖에 생각나지 않아요. 1. Client <-> Server 데이터를 주고받을 때, (dto)Server <-> DB 데이터를 주고받을 때, (Entity)하지만 DTO나 Entity를 싱글톤으로 만들어줄 이유가 없으므로 직렬화/역직렬화 때문에 싱글톤이 깨지는 경우가 발생할까가 의문입니다. <질문 정리>리플렉션이나 이용해서 객체를 생성하는 경우가 실무에서 종종 사용되는 방법 인가요?직렬화/역직렬화가 이루어지는 클래스는 싱글톤으로 설정할만한 클래스가 아닐 텐데 직렬화/역직렬화 때문에 싱글톤이 깨지는 경우가 실무에서 종종 발생하는 문제인가요?1~2번 질문에 대한 답이 만약 저렇게 객체를 생성하는 경우는 없다 라면 싱글톤을 구현할 때 저런 것을 고려하며 짤 이유가 있나요?
-
미해결스프링 핵심 원리 - 고급편
어떻게 ProxyFactory() 생성시 인터페이스가 있는 target과 없는 target을 구분하는지 궁금합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]new ProxyFactory()생성시 내부 로직의 생성자에는 setTarget(target);과setInterface(ClassUtils.getAllInterfaces(target)); 으로 이루어져있고 setInterface(...)의 로직에는 Assert.notNull으로 인터페이스가 없을 경우 exception을 발생하게 되어있던데 어떻게 인터페이스가 없는 target으로 ProxyFactory가 생성되는지 궁금합니다.
-
미해결스프링 핵심 원리 - 고급편
Parent는 굳이 빈을 등록할 필요가 없는 이유가 궁금합니다.
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]본 강의 6:20초 쯤, "Parent는 굳이 @Bean으로 등록할 필요가 없겠네요." 라고 말씀해주셨는데, 이 부분이 잘 이해가 안 되어 문의드립니다!저희가 지금까지 배웠던 AOP는 스프링의 어플리케이션 컨텍스트에 객체를 @Bean으로 등록할 때, 실제 객체 대신에 프록시 객체를 등록하는 방식으로 동작했었는데,만약 Parent 객체를 @Bean으로 등록하지 않는다면 @target 을 통한 parentMethod 호출 시에도 Advice의 부가기능이 실행되지 않는 것 아닌가요~? 제가 잘못 이해하고 있는 부분이 있다면 짚어주시기를 부탁드립니다!
-
미해결스프링 핵심 원리 - 고급편
LogTrace의 Bean을 메인 클래스에서 생성한 이유
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? [질문 내용]LogTrace의 @Bean 어노테이션으로 빈 생성할 때 메인 클래스에서 생성한 이유가 따로 있으신가요??Config 클래스에서 하지 않고 따로 메인 클래스에서 하신 이유가 있으신가 해서요@Bean 어노테이션은 다른 클래스에서도 사용이 가능한지도 궁금합니다
-
미해결스프링 핵심 원리 - 고급편
@target 관련
안녕하세요 강의 잘 듣고 있습니다.강의 말미에 args, @args, @target은 단독으로 사용하면 안 된다고 하셨고 다음과 같은 이유를 말씀하셨습니다. '실제 객체 인스턴스가 생성되고 런타임에 적용 여부를 알 수 있다'아규먼트의 실제 타입을 요구하는 args, @args는 비교적 직관적으로 이해했는데, @target은 어째서 런타임에 적용 여부를 알아야만 하는 걸까요? 마치 @within이 그런 것처럼 클래스 내부에 특정 어노테이션이 있으면 해당 클래스부터 부모 클래스까지 AOP를 적용하면 될 것 같은데 말이죠.
-
해결됨스프링 핵심 원리 - 고급편
포인트컷 표현식 질문
안녕하세요 강의 잘 듣고있습니다.강의에서 등장하는 AtTargetAtWithinAspect의 execution 표현식을 보면 * hello.aop..*(..)로 기재되어 있습니다.이전 강의에서 학습했던 표현식(* hello.aop..*.*(..))과 어떤 차이점이 있는건가요?
-
해결됨스프링 핵심 원리 - 고급편
@AfterReturning에서 질문있습니다.
안녕하세요 강사님!강사님께서 말씀해주신 AOP로 구현하면 좋은 기능을 실제 코드로 작성해봤습니다.로그 트레이스기능에 매서드 종료가 1초 5초 10초가 될때 로그 레벨을 높여서 로그를 찍는 기능입니다. @Aspect @Slf4j static class LogTraceSelf { ThreadLocal<TraceStatus> trace = new ThreadLocal<>(); private TraceStatus SynchronizedTrace(){ TraceStatus traceStatus = trace.get(); if (traceStatus == null) { trace.set(new TraceStatus()); traceStatus = trace.get(); } return traceStatus; } @Pointcut("@annotation(hello.aop.exam.annotation.Trace)") public void logTrace() {} @Before("logTrace()") public void doTrace(JoinPoint joinPoint) { TraceStatus traceStatus = SynchronizedTrace(); traceStatus.begin(joinPoint.getSignature().getName()); } @AfterReturning("logTrace()") public void doTraceSuccess(JoinPoint joinPoint) { TraceStatus traceStatus = SynchronizedTrace(); long gap = traceStatus.end(joinPoint.getSignature().getName()); } @AfterThrowing(value = "logTrace()", throwing = "ex") public void doTraceException(JoinPoint joinPoint, Exception ex) { TraceStatus traceStatus = SynchronizedTrace(); traceStatus.end(joinPoint.getSignature().getName(),ex); } }조인포인트 실행 권한이 필요없는 부가 기능이다보니@Around 사용을 하지 않고 나누어서 처리를 하다보니 불필요한 코드가 더 증가하게되었습니다.그리고 @Around와 다르게 어드바이스내 지역변수에 TraceStatus를 저장하고 사용할수 없기때문에ThreadLoacal에 공유할 데이터와 본인이 가지고 있어야하는 timestamp도 저장했습니다.@Slf4j public class TraceStatus { private final String id; private int depth = 0; private Deque<Long> timeStampHolder = new ArrayDeque<>(); private final int offsetInfoMs; private final int offsetWarningMs; private final int offsetErrorMs; }기능은 stack 자료구조를 활용해서 begin을 할때마다 timeStampHolder에 시작시간을 저장합니다.end를 호출하면 pop으로 자신이 넣은 시작 시간을 꺼내서 시간을 계산하고 로그를 출력합니다.기타 기능 메서드들은 강사님이 작성하신거와 유사해서 제외했습니다. 이제 소요된 시간이 1초, 5초, 10초가 걸릴 경우 info,warning,error로 출력하는 로그를 남기려고할때어드바이스가 조건을 가지고 추가 기능을 만든다.위 코드처럼 로그 트레이스 내에 저장하고 로그 트레이스 내부에서 출력하도록 한다.경고 알림용 트레이스를 따로 만들고 상태를 전달한다.제 생각은 트레이스에게 넘길경우 트레이스를 인터페이스로 만들어서 필요에 따라 다른 트레이스를 사용한다고 한다면 이 기능을 계속 구현해야 된다는 단점이 생길거 같습니다.저라면 어드바이스에서 소요시간을 트레이스에게 받아서 추가 로직을 수행하려고 할거같습니다. 강사님께서 코드를 작성하신다면어떤 포인트컷을 사용하셔서 구현하실건지,그리고 애노테이션에서 넘어온 값을 가지고 어드바이스에서 처리하게 하시는지아니면 로그 트레이스 내에서 처리하게 하실건지 그 이유가 궁금합니다.
-
해결됨스프링 핵심 원리 - 고급편
Enhancer.setSuperclass
/** * Set the class which the generated class will extend. As a convenience, * if the supplied superclass is actually an interface, <code>setInterfaces</code> * will be called with the appropriate argument instead. * A non-interface argument must not be declared as final, and must have an * accessible constructor. * @param superclass class to extend or interface to implement * @see #setInterfaces(Class[]) */ public void setSuperclass(Class superclass) { if (superclass != null && superclass.isInterface()) { setInterfaces(new Class[]{superclass}); // SPRING PATCH BEGIN setContextClass(superclass); // SPRING PATCH END } else if (superclass != null && superclass.equals(Object.class)) { // affects choice of ClassLoader this.superclass = null; } else { this.superclass = superclass; // SPRING PATCH BEGIN setContextClass(superclass); // SPRING PATCH END } } 강사님 PDF 5.동적 프록시 기술 마지막 장인터페이스가 있는 경우에는 JDK 동적 프록시를 적용하고, 그렇지 않은 경우에는 CGLIB를 적용하려면어떻게 해야할까?두 기술을 함께 사용할 때 부가 기능을 제공하기 위해서 JDK 동적 프록시가 제공하는InvocationHandler 와 CGLIB가 제공하는 MethodInterceptor 를 각각 중복으로 만들어서 관리해야할까?라고 작성되어 있더라구요진짜 구현 클래스만 프록시를 만들어 주는지 궁금해서 찾아보니Enhancer 클래스 내부 메서드를 보면 setSuperclass 가 있는데내부에 인터페이스가 들어오던, 구체 클래스가 들어오던 알아서 구현체를 만들어준다고 되어있더라구요Enhancer 클래스 주석에도하지만 프록시가 인터페이스를 구현하는 것 외에 구체적인 기본 클래스를 확장할 수 있게 해준 것입니다.라고 작성되어 있습니다. 실제 코드로 작성해도 잘 동작합니다.package hello.proxy.my; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; @Slf4j public class EnhancerTest { @Test void enhancerInterface() { MyInterfaceImpl myInterface = new MyInterfaceImpl(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyInterface.class); enhancer.setCallback(new MyInterceptor(myInterface)); MyInterface myInterfaceProxy = (MyInterface) enhancer.create(); myInterfaceProxy.call(); } static class MyInterceptor implements MethodInterceptor { private final MyInterface myInterface; public MyInterceptor(MyInterface myInterface) { this.myInterface = myInterface; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { log.info("로그 시작"); myInterface.call(); log.info("로그 종료"); return null; } } } interface MyInterface { void call(); } @Slf4j class MyInterfaceImpl implements MyInterface { @Override public void call() { log.info("나는 인터페이스를 구현한 클래스입니다."); } } //log 19:11:27.315 [Test worker] INFO hello.proxy.my.EnhancerTest - 로그 시작 19:11:27.317 [Test worker] INFO hello.proxy.my.MyInterfaceImpl - 나는 인터페이스를 구현한 클래스입니다. 19:11:27.317 [Test worker] INFO hello.proxy.my.EnhancerTest - 로그 종료Enhancer 클래스도 인터페이스나 구체 클래스를 모두 프록시로 생성할 수 있고더 향상된 호출 핸들러을 제공하는 MethodInterceptor 를 사용할 수도 있습니다.제 생각에는 MethodInterceptor 사용할 수 있는 Enhancer 프록시 객체를 디폴트로 사용할거같거든요 그런데 ProxyFactory는 false로 인터페이스랑 구체 클래스를 분리해서 생성하게 했더라구요 그 이유도 궁금합니다.제가 이해한게 맞다면 Enhancer도 인터페이스를 구현할 수 있는데 별도로 개발해야한다고 말씀하신 이유도 궁금합니다 !!
-
미해결스프링 핵심 원리 - 고급편
어드바이저 빈에 대해서는 프록시를 적용하지 않나요?
자동 프록시 생성기는 빈이 후처리기로 들어오면 어드바이저 빈과 후처리기로 들어온 빈을 매칭해서 프록시의 생성여부를 결정하는 걸로 이해했습니다.그러면 처음에 어드바이저 빈을 생성할 때에는 자동 프록시 생성기가 '얘는 어드바이저 빈이구나. 프록시를 생성할 필요가 없겠어' 라고 판단하고 걸러주는 건가요?
-
미해결스프링 핵심 원리 - 고급편
실행순서
안녕하세요실행 순서: @Around , @Before , @After , @AfterReturning , @AfterThrowing 라고 적어주셨는데 @After 이게 왜3 번째로 실행된다고 표현 해주신게 이해가 조금 안갑니다. 실제로는 @AfterReturnin 다음에 실행되는거 아닌가요 ?
-
미해결스프링 핵심 원리 - 고급편
setProxyTargetClass()
문서에는 true가 기본적으로 설정해서 사용한다고 적혀있는데 default는 false 아닌가요?? 그냥 true를 기본적으로 많이 쓴다는 소리일까요 ?
-
미해결코딩으로 학습하는 GoF의 디자인 패턴
코드에 약간 오타가 있는거 같습니다.
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.안녕하세요. 백기선님수업을 잘 듣고 있습니다. 제 생각에 간단한(?) 오타같은게 있는거 같아서 제보드립니다. 혹시 오타가 아니라면 제가 잘못 이해한 부분을 말씀 부탁드리겠습니다.옵저버 패턴 3부 - 1:56초쯤 unregister 를 subject가 "디자인패턴"인 것에 대해서 user2 를 해지 해주었는데요.이미 user 2는 오징어게임에만 등록중입니다. 그래서 결과값도 user2 가 보낸 "옵저버 패턴 장, 단점 보는 중" 이 user1한테 가서 출력된거 같습니다.아마 user1을 해지하거나 user2를 오징어게임에 대해서 해지하려고 하지 않았나 싶습니다..