묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
QuerryDSL은 JPA기술에서만 적용이 가능한가요? + 여러가지 질문이 있습니다.
JPA소개2 강의에서 여러 프로젝트의 DB관련 기술을 보면 QueryDSL을 사용한다고 설명해주셨는데 이전 다른 기술 강의에서도 queryDSL은 동적쿼리를 자동으로 생성해준다 라고 들었습니다. 마이바티스나 jdbc템플릿에서는 queryDSL을 사용하지 못하는건가요?+ 그리고 프로젝트를 해보려고 하는데 jpa는 내용이 많은것 같아 MyBatis를 먼저 사용해서 프로젝트를 진행하고 추후에 영한님의 다른jpa강의 로드맵을 수강하면서 프로젝트를 수정해볼려고 생각중입니다. 그것과 관련된 질문으로 섹션8부터 있는 내용들을 일단 건너뛴 후 프로젝트를 진행하고 나서 나머지 섹션을 수강하고 로드맵의 스프링 로드맵의 핵심원리 고급편과 핵심원리 활용을 수강하여도 학습에 무리가 없을까요? 아니면 섹션 8내용은 일단 건너뛴 후 핵심원리 활용과 고급편을 수강은 일단 하는게 좋은가요?
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
hello-mvc에서 view에서 인식을 못하네요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.hello-mvc?name값으로 넘겨줄려는데 v부분에서 인식을 못합니다!
-
미해결Kevin의 알기 쉬운 Spring Reactive Web Applications: Reactor 1부
DROP 전략과 LATEST 전략의 차이점이 무엇인가요?
안녕하세요. 수업 잘 듣고 있습니다~백프레셔 전략 중 DROP과 LATEST 전략은 결국 버퍼가 비워질 때까지 Publsiher에서 emit되는 데이터를 제거(drop or discard)하는 것으로 이해했습니다. 버퍼가 비어지는 시점 이후로 emit되는 데이터를 다시 버퍼에 채운다는 점에서 두 전략은 같은 것으로 보입니다. 차이점을 알려주시면 감사하겠습니다~
-
미해결실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
'다대다' 속성 value 타입은 'Category'일 수 없습니다. 오류
안녕하세요 강의를 따라하던중 다음과 같은 오류가 발생합니다.교안과도 같은 코드이고 인텔리제이도 껐다 켰는데 왜 이런 문제가 발생하는지 모르겠습니다.
-
미해결견고한 결제 시스템 구축
완강!!!!!!!!!!!!!!
드디어 완강!! 🥳유익한 강의 잘들었습니다.감사합니다.
-
미해결재고시스템으로 알아보는 동시성이슈 해결방법
중간테이블에 대한 낙관적 락 적용법
현재 Member 테이블과 Appointment 테이블이 존재하는데, N:N 관계이기 때문에 아래와 같이 AppointmentUser라는 중간 테이블이 존재합니다.@Entity @Table(name = "appointment_and_user") public class AppointmentUser extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "apppointment_id", nullable = false) private Appointment appointment; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) private Member member; @Enumerated(value = EnumType.STRING) @Column(name = "member_authority", nullable = false) private MemberAuthority memberAuthority; @Version private Long version;하나의 AppointmentUser 테이블은 약속과 멤버의 id를 하나씩 가집니다. 여기서 레포지토리의 코드는 이러합니다.public interface AppointmentUserRepository extends JpaRepository<AppointmentUser, Long> { ... @Lock(LockModeType.OPTIMISTIC) @Query("select au from AppointmentUser au where au.id = :id") AppointmentUser findByIdWithOptimisticLock(Long id);findByIdWithOptimistic 메서드를 통하여 특정 유저-약속 테이블의 데이터를 통해 AppointmentUser 객체를 반환합니다. 서비스 계층의 코드는 아래와 같습니다.@Transactional public void updateAuthority(Long appointmentId, Long loginMemberId, Long targetMemberId) { Member loginMember = memberRepository.getById(loginMemberId); Member targetMember = memberRepository.getById(targetMemberId); Appointment appointment = appointmentRepository.getById(appointmentId); AppointmentUser loginAppointmentUser = appointmentUserRepository.getByMemberAndAppointment(loginMember, appointment); AppointmentUser targetAppointmentUser = appointmentUserRepository.getByMemberAndAppointment(targetMember, appointment); appointment.changeTitle("asd"); validateIsAdminMember(loginAppointmentUser.getId()); MemberAuthority targetAuthority = targetAppointmentUser.getMemberAuthority(); targetAppointmentUser.updateAuthority(MemberAuthority.getAnotherAuthority(targetAuthority)); appointmentRepository.save(appointment); appointmentUserRepository.save(targetAppointmentUser); } private void validateIsAdminMember(Long loginAppointmentUserId) { if (appointmentRepository.findByIdWithOptimisticLock(loginAppointmentUserId).getMemberAuthority() != MemberAuthority.ADMIN) { throw new NotAdminMemberException(); } } <로직 설명>하나의 약속 내에 멤버 두명이 존재각 멤버들은 ADMIN or NORMAL 권한을 갖고 있음.두명 다 약속 내 에서 ADMIN 권한을 갖고 있다는 상황을 가정. (ADMIN은 AppointmentUser 엔티티의 MemberAuthority 라는 필드의 Enum 값입니다.)두명이 서로를 동시에 ADMIN에서 NORMAL로 권한을 박탈하는 경우, 하나의 약속 안에 ADMIN인 사람이 없어지는 예외적인 문제 상황이 발생함.그래서 @Version을 AppointmentUser 엔티티의 필드로 등록하여 해결하려고 했으나..사용자 A와 B가 있다고 할 때 service 코드 내의 validateIsAdminMember 메서드를 통해 상대방의 권한을 박탈하려는 유저(본인)가 ADMIN인지 검증하여 ADMIN이 맞다면 박탈하고, ADMIN이 아니라면, 예외를 던지게 끔 하는 로직에서,레포지토리 내의 findByIdWithOptimisticLock 메서드를 동시에 접근했을때 Version 필드를 통해 동시성 문제를 제어할 수 있다고 생각했으나..validateIsAdminMember로 검증하는 A와 B의 AppointmentUser 엔티티는 서로 다른 객체(데이터)이기 때문에 서로 다른 테이블의 Version 값을 변경하기 때문에 동시성 보장이 안됨..그래서 Appointment에 Version필드를 넣어주려 했지만, Version값은 해당 테이블에 변화가 생겨야 변한다.하지만 로직상, AppointmentUser(중간테이블)에 변화가 생기는게 맞다...위와 같은 중간 테이블 사용으로 인한 문제가 발생하였을 때 어떻게 강의자님이시라면 어떻게 해결하실지 궁금합니다!
-
미해결재고시스템으로 알아보는 동시성이슈 해결방법
version 컬럼이 증가하지 않는 이유
현재 강의 내용을 바탕으로 OptimisticLock 을 구현중인데,version이 증가하지 않는 이유를 알고 싶습니다.도메인은 이렇습니다.@Entity @Table(name = "appointment") public class Appointment extends BaseEntity { ... @OneToMany(mappedBy = "appointment") private List<AppointmentUser> appointmentUsers = new ArrayList<AppointmentUser>(); @Version private Long version; 그리고, 레포지토리는 이렇습니다.public interface AppointmentRepository extends JpaRepository<Appointment, Long> { ... @Lock(LockModeType.OPTIMISTIC) @Query("SELECT au FROM Appointment a " + "JOIN a.appointmentUsers au " + "WHERE au.id = :appointmentUserId ") AppointmentUser findByIdWithOptimisticLock(Long appointmentUserId); } 그리고, 서비스 계층 메서드는 이렇습니다.@Transactional(readOnly = true) @Service public class AppointmentUserService { ... @Transactional public void updateAuthority(Long appointmentId, Long loginMemberId, Long targetMemberId) { Member loginMember = memberRepository.getById(loginMemberId); Member targetMember = memberRepository.getById(targetMemberId); Appointment appointment = appointmentRepository.getById(appointmentId); AppointmentUser loginAppointmentUser = appointmentUserRepository.getByMemberAndAppointment(loginMember, appointment); AppointmentUser targetAppointmentUser = appointmentUserRepository.getByMemberAndAppointment(targetMember, appointment); validateIsAdminMember(loginAppointmentUser.getId()); MemberAuthority targetAuthority = targetAppointmentUser.getMemberAuthority(); targetAppointmentUser.updateAuthority(MemberAuthority.getAnotherAuthority(targetAuthority)); appointmentUserRepository.save(targetAppointmentUser); } private void validateIsAdminMember(Long loginAppointmentUserId) { if (appointmentRepository.findByIdWithOptimisticLock(loginAppointmentUserId).getMemberAuthority() != MemberAuthority.ADMIN) { throw new NotAdminMemberException(); } } }위 updateAuthority 메서드 내에서 validateIsAdminMember를 호출하여, validateIsAdminMember 안에 있는 findByIdWithOptimisticLock을 통하여 Version을 올려주어서 낙관적 락을 성공적으로 구현할 수 있을 줄 알았는데 테스트를 해보니 아래와 같이 실패합니다.. @Test void 동시성_테스트() throws InterruptedException { Logger logger = Logger.getLogger(MultiThreadTest.class.getName()); ExecutorService executorService = Executors.newFixedThreadPool(2); CountDownLatch latch = new CountDownLatch(2); AtomicReference<Boolean> flag = new AtomicReference<>(false); executorService.execute(() -> { try { logger.log(Level.INFO, "첫 번째 요청 시작: member1 -> member2"); logger.log(Level.INFO, "버전1전" + appointmentRepository.getById(1L).getVersion()); appointmentUserService.updateAuthority(1L, 1L, 2L); logger.log(Level.INFO, "버전1완" + appointmentRepository.getById(1L).getVersion()); logger.log(Level.INFO, "첫 번째 요청 완료: member1 -> member2"); } catch (Exception e) { logger.log(Level.SEVERE, "첫 번째 요청 중 예외 발생", e); flag.set(true); } finally { latch.countDown(); } }); executorService.execute(() -> { try { logger.log(Level.INFO, "두 번째 요청 시작: member2 -> member1"); logger.log(Level.INFO, "버전2전" + appointmentRepository.getById(1L).getVersion()); appointmentUserService.updateAuthority(1L, 2L, 1L); logger.log(Level.INFO, "버전2완" + appointmentRepository.getById(1L).getVersion()); logger.log(Level.INFO, "두 번째 요청 완료: member2 -> member1"); } catch (Exception e) { logger.log(Level.SEVERE, "두 번째 요청 중 예외 발생", e); flag.set(true); } finally { latch.countDown(); } }); latch.await(); };위와 같이, 두 개의 스레드에서 진행을 하였고, 각 스레드 내에서 updateAuthority()를 실행 전후에 version을 찍어보았으나, 찍힌 4개의 version 모두 0이 나왔고 또한, 동시성 제어도 안되는 상황입니다.왜 0이 나오는걸까요.. 궁금합니다!(컴파일 에러는 없습니다)
-
미해결실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
실무에서 매핑 테이블은 어떻게 활용할 수 있을까요?
토이 프로젝트를 진행하던 중 궁금한 내용이 생겨서 질문 드립니다.과거 Mybatis와 같은 것을 활용해 진행할 때는코드 매핑 테이블에 코드값 (기본키)과 코드명 이렇게 있고특정 테이블에서는 이러한 코드 값 키를 들고 있어 서브쿼리를 활용해 코드 명을 가져오는 방식으로 활용했습니다.JPA에서 위 구조를 활용하려면 특정 엔티티에 코드 엔티티를 넣고 서로 연관관계를 걸어 준 후에 fetch join으로 select 하는게 베스트 일까요?
-
미해결Kevin의 알기 쉬운 Spring Reactive Web Applications: Reactor 1부
백프레셔 전략 관련해서
폐기와 드랍의 차이를 정확히 모르겠습니다. subscriber 입장에선 버퍼가 다시 비어져 있을 때 처리는 똑같아 보이는데 폐기는 publisher 에서 데이터 삭제를 의미하고 drop 은 데이터 건너 뛰기로 이해 하면 될까요?
-
해결됨스프링 DB 2편 - 데이터 접근 활용 기술
Q타입 생성 확인 오류 질문입니다.
[질문 내용]pdf 파일을 보며 강의 듣기전 예습을 하고 있습니다.build.gradle 파일에 Querydsl 관련 설정을 pdf파일에 나와 있는 대로 밑에와 같이 추가했습니다.//Querydsl 추가implementation 'com.querydsl:querydsl-jpa'annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"annotationProcessor "jakarta.annotation:jakarta.annotation-api"annotationProcessor "jakarta.persistence:jakarta.persistence-api" (현재 저는 Gradle을 통해서 빌드중입니다) Gradle -> Tasks -> build -> clean실행 시에는 문제가 없는데Gradle -> Tasks -> other -> compileJava실행 시에Unable to load class 'javax.persistence.Entity'.This is an unexpected error. Please file a bug containing the idea.log file. 라는 오류가 뜹니다. 그래서 커뮤니티에 관련 질문들을 찾아 보았는데,build.gradle 파일에서 Querydsl 을 설정하는 부분을implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:$ {dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api"로 바꿔보라고 하신 내용이 있어서 바꿔보았습니다.그리고 gradle refresh를 눌렀을 때 오류가 발생했는데 지금은 되네요... pdf 파일 내용을 수정해주시면 감사하겠습니다!!
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
버젼 관련 문제 인거 같은데 도와주세요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요이런 오류가 나는데.. 어떤 오류인지 잘 모르겠어요.. 버젼 관련인거 같은데 gradle.properties에 distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 이걸로 바꾸긴 했는데 잘 되다가 갑자기 안되서 혹시 이거 문제 일까요??
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
itemservice-db 프로젝트
controller가 없는데 Front만으로도 controller처럼 처리를 할 수 있는건가요?아니면 이전에 memory에서 해서 모델에서 변환시켜줬어야 했는데, db를 이용하니까 model view controller가 다 필요없어진 건가요? items.html에서 이렇게 코드가 있는데th:onclick="|location.href='@{/items/add}'|" 이게 addForm.html로 가는 건가요?어디에서 변환이 되는건지 모르겠는데 관련 코드좀 찾아주실 수 있나요?
-
미해결스프링 시큐리티 완전 정복 [6.x 개정판]
Security 에서 사용되는 Filter 의 @Bean 등록에 관하여 질문있습니다.
안녕하세요 선생님, 제가 읽은 spring security 문서와 제공해주신 ppt 의 내용에 약간 다른 점이 있어서 질문드립니다. [인증 관리자 - AuthenticationManager - 1] 강의 20:54 에 나오는 ppt 에는 CustomAuthenticationFilter 를 @Bean 으로 등록하라고 나옵니다. 그런데 제가 읽은 spring security 문서에서는 spring security 전용 Filter 는 단순히 @Bean 으로만 등록하면 servlet container 에 의해서 자동으로 Filter 로 등록되버리기 때문에 FilterRegistrationBean 까지 사용해서 이를 우회하라는 글을 본적이 있습니다. 참고: Architecture :: Spring Security 혹시 ppt 오타일까요?
-
미해결
spring GCP 배포 mysql 문제
현재 spring boot 프로젝트를 GCP를 통해 배포하는 중인데요,rds 만들어서 연동도 잘 해두었고 ssh로 들어갔을때 mysql 연동도 잘 됩니다.근데 jar 파일로 서버를 실행하려고 할 때 이런 에러가 나더라구요ㅜㅜ 찾아보니까 mysql 연동 문제라고 하던데 아무리 봐도 모르겠습니다...제발 도와주세요ㅜㅜㅜ
-
해결됨실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
주문조회 강제 초기화 질문
@GetMapping("/api/v1/orders") public List<Order> ordersV1() { List<Order> all = orderRepository.findAll(); for (Order order : all) { order.getMember().getName(); //Lazy 강제 초기화 order.getDelivery().getAddress(); //Lazy 강제 초기화 List<OrderItem> orderItems = order.getOrderItems(); orderItems.stream().forEach(o -> o.getItem().getName()); //Lazy 강제 초기화 } return all; }첫 질문Lazy방식이기에 조인되어 있는 필드를 가져오지 못하기에 member와 delivery는 One이기 때문에 get으로 초기화 하고orderItem은 Many이기 때문에 List형식으로 stream으로 초기화 하는걸로 이해해도 될까요?강제 초기화를 하는 이유는 값을 넣어주기 위해..? 인가요..? 두번째 질문orderItems.stream().forEach(o -> o.getItem().getName());이 부분 코드 제가 이해한게 맞는지 확인부탁드립니다.o(orderItems 컬렉션에서 하나하나)에서 getItem().getName()를 가져와서 반환할 것이다.근데 getName(이름 가져오기)만 하는데 Item의 모든 api값을 가져오는것 같아서 질문드립니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
뷰 리졸버 관련 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]여기에 질문 내용을 남겨주세요. 안녕하세요. 뷰 리졸버 강의 듣고 있는 도중, properties 파일에 suffix와 prefix 설정 후 포워드 시도했지만 계속 404페이지가 뜨더라구요. 제 인텔리제이가 커뮤니티 버전인데, 커뮤니티 버전은 properties 파일이 안되서 그런건지 궁금합니다. 우선 혹시 모르니 아래에 강의 관련된 내용의 깃 올려놓겠습니다. 감사합니다. https://github.com/bukwon/Spring-MVC/commit/091c8ff510405fdde8d470ec3a7a51e1b59de58a
-
미해결
동시성 처리 관련 스레드 풀 설정 질문
@Test @DisplayName("쿠폰 여러 명 발급") void 쿠폰_여러_명_발급() throws InterruptedException { int threadCount = 1000; ExecutorService executorService = Executors.newFixedThreadPool(32); CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { final int threadNumber = i + 1; Integer key = i; executorService.submit(() -> { try { couponService.issueCoupon(param, usersMap.get(key)); } catch (PessimisticLockingFailureException e) { .... }쿠폰 발급 동시성 처리 관련해서 테스트 코드 작성 간 궁금한 점이 있어서 질문을 올립니다. 구글링해서 작성해봤는데, 제가 이해하는 게 맞나 싶어서요... Executors.newFixedThreadPool(32) 이렇게 설정해주면, 32 개의 고정된 스레드 풀을 생성한다는 것이고 1,000 명의 유저가 해당 스레드 풀이 나눠서 작업이 수행된다는 것인가요 ? 그러니까 하나의 스레드에서 약 31명의 유저를 담당한다는 뜻일까요 ? 아니면 순차적으로 1,000 명의 유저를 하나의 스레드에 한 명씩 배치하여 작업하는 것이고, 실질적으로 한 순간에 32명의 유저만 작업한다는 뜻일까요 ? ㅠㅠㅠ
-
미해결스프링부트 시큐리티 & JWT 강의
강사님께서 말씀하시는 시큐리티세션이 SecurityContext인가요?
시큐리티세션이 SecurityContextHolder가 제공하는 SecurityContext를 말씀하시는걸까요..?
-
미해결스프링 시큐리티 완전 정복 [6.x 개정판]
RestAPI를 제공하는 서버에서 Redis + Session을 통해 인증을 시도하고 있는데 직렬화가 되지 않습니다.
안녕하세요 강의 잘 보고 있습니다.저는 지금 Rest API서버를 생성하고 인증을 하는 방식은 session으로 하고 session 저장소를 Redis로 옮겨 구현하고자 합니다. 그러나 강의와는 다르게 @EnableRedisHttpSession 어노테이션을 추가해도 Redis에 저장이 되지 않는 오류가 있는데 이를 어떻게 해결해야할지 모르겠어 질문을 드립니다. Redis가 아닌 Memory에 저장을 하였을때는 정상 동작하였으며 디버깅을 통해 문제의 원인을 파악해본 결과 SecurityContextImpl 객체에 대한 직렬화를 수행해주는 클래스가 존재하지 않아 오류가 발생하는 듯 합니다. 혹시 이 문제에 대해 도움을 받을 수 있을까요? 문제상황을 작성해놓은 포스팅과 프로젝트의 압축파일을 첨부해드리도록 하겠습니다.추가로 프로젝트 환경은 로컬에서 진행하였으며 H2 DB와 Redis를 사용하였습니다.https://goto-pangyo.tistory.com/286https://drive.google.com/file/d/1DccNwJWmUPBpe3KwsqxPp_VpYIAQRRaR/view?usp=sharing
-
미해결스프링 핵심 원리 - 고급편
interfaceProxy에 대해서 질문 있어요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]인터페이스 프록시들이 잘 살펴보면 조금씩은 차이가 있지만 비슷한데 이것들을 전략패턴이나 아니면 다른 방식으로 또 통합 가능한가요?