묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
Service 소형 테스트 질문
서비스를 소형 단위로 테스트하기 위해 Fake 클래스를 구현하는 실습을 진행하셨는데요.테스트 코드는 결국 구현된 기능이 정상적으로 동작하는지 검증하기 위한 것이라고 생각하는데요~그런데 H2 DB를 직접 띄워 테스트하는 방식과 비교했을 때, Fake 객체를 활용한 방식은 구현 방식에 따라 실제 동작과의 괴리가 생길 수도 있을 것 같은데, 이런 접근이 실제로 효과적인 테스트 방법인지 궁금합니다!
-
미해결Practical Testing: 실용적인 테스트 가이드
ERD 가장자리에 있는 도메인 테스트 질문
ERD 가장자리에 있는 도메인들은 선행적으로 존재해야하는 도메인들이 있다보니 테스트 코드 작성 시 given 부분이 너무 길어져요.어떻게 짜는게 좋은지 의견 부탁드립니다!!
-
해결됨오브젝트 - 설계 원칙편
디자인 패턴에 대해선 어떤 생각을 가지고 계신지 궁금합니다.
좋은 강의 감사합니다. 강의를 들으면서 객체지향 설계 원칙에 맞춰 코드를 짜다보면 나중에 되돌아봤을때 의도했던 안했던 시중에 알려져있는 디자인 패턴과 유사한 방향으로 코드가 만들어질 것 같다는 생각을 했습니다. 강사님께서는 디자인패턴을 어떻게 바라보고 계시고 디자인 패턴을 의도해가며 구현할 때와 아닐때가 있으신지도 궁금합니다~!!감사합니다~!!
-
해결됨오브젝트 - 설계 원칙편
8-5 책임 분리를 통한 중복 코드 제거 과정에서 특정 조합은 불가능할 때
안녕하세요!AbstractReader로부터 파싱에 대한 책임을 분리하여 Parser라는 클래스를 새로 생성하는 내용 잘 들었습니다!여기서 궁금한 것이 생겨 질문 남깁니다..!DatabaseReader, RedisReader, FileReader / CsvParser, JsonParser, XmlParser 구성은 총 9가지의 경우의 수가 나올 수 있을 것 같은데요만약 '(DatabaseReader, XmlParser)는 불가능하고 (DatabaseReader, CsvParser)와 (DatabaseReader, JsonParser)가 가능하고,(RedisReader, XmlParser)와 (RedisReader, CsvParser)는 불가능하고 (RedisReader, JsonParser)만 가능하다' 와 같이 특정 조합이 불가능한 경우는 어떻게 컴파일 타임에 가능한 조합으로만 강제할 수 있을지 궁금합니다!
-
미해결실무에 바로 적용하는 프런트엔드 테스트 - 2부. 테스트 심화: 시각적 회귀・E2E 테스트
e2e 테스트 CI , 서버비용
안녕하세요 e2e 테스트는 비용이 많이 들어서 어떻게 관리를 하는지 궁금합니다. - 파이프라인에서 매번 CI 에서 돌리기에는 서버나, 시간이 개발 생산성을 잡아 먹을 것 같은데요. 점점 e2e 테스트가 쌓여 나갈떄 어떤 전략을 취할 수 있는지 궁금해요. GPT로 리서치하니까 보통 2가지를 병행해서 스케줄러 + 온디맨드 실행 같이 관리 한다고 하는데, 실제 현업에서는 어떻게 활용하고 계신지, 제가 리서치한 내용 말고 더 효율적이거나, 추천할만한 전략 , 이외의 고려사항 소개해주실 수 있으면 알려주시면 감사하겠습니다.스케줄 실행 (Nightly/주말 풀스윗)EventBridge → ECS Fargate RunTask(Spot 가능) → Playwright 러너대상: 스테이징(or PR 프리뷰 URL)b. 산출물: 트레이스/비디오/리포트 S3 업로드, Slack에 요약/링크온디맨드 실행 (PR 코멘트/수동 트리거)Bitbucket Pipeline에서 aws ecs run-task 호출(또는 ChatOps 슬래시 커맨드)태그/폴더/샤드 인자 넘김 → 필요한 부분만
-
해결됨Pytest와 Nox를 활용한 파이썬 테스트 자동화 완벽 가이드
nox 실행 에러 메세지
챕터 17 nox 테스트 중입니다. 테스트 환경은 윈도우11, 파이썬 3.11버전이고 공유주신 noxfile.py 의 tests 세션을 실행하던 중에 다음 에러가 발생하였습니다. session.install("-e", ".") 실행중에 에러가 난것으로 보이는데 setup.py 나 pyproject.toml 이 없다고 나오는데 공유주신 자료에는 안보여서요.현재 프로젝트 파일 구조는 my_pytest / mycalc/my_pytest / tests /my_pytest / noxfile.pymy_pytest / requirements-dev.txt 로 되어 있습니다.
-
미해결Java/Spring 주니어 개발자를 위한 오답노트
Repository 인터페이스 위치
class Controller, class Service, Interface Repository, class RepositoryImpl, Interface JpaRepository의 그림에서 Interface Repository 가 Persistence 가 아닌 Business 계층으로 분리하신 이유가 궁금합니다!
-
미해결Practical Testing: 실용적인 테스트 가이드
DTO 검증 필드에 대한 테스트 코드 작성은 어디까지?
DTO의 검증 필드마다 테스트코드를 작성하는게 실무에서 일반적인가요? 이렇게 되면 DTO가 커질 수록 DTO 한개당 테스트 함수가 10~11개 이렇게 필드개수 만큼 나오게 될텐데 실제로 모두 테스트코드로 검증하나요?
-
미해결Practical Testing: 실용적인 테스트 가이드
OrderCreateRequest DTO에 대해서 궁금한점
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. Business Layer 테스트(1)의 13:32초 즈음에서 OrderCreateRequest가 Product의 id값이 아니라 productNumber를 요청 dto로 받았는데 그 이유가 있나요? 저라면 id를 받도록 설계할거같은데 이유가 궁금합니다. 실무에서는 보통 저렇게하나요?
-
해결됨오브젝트 - 설계 원칙편
6-2. 명령과 쿼리 분리 원칙 질문
6-2. 명령 쿼리 분리 원칙으로 부수효과 관리하기에 관해 궁금한 점이 있어 질문드립니다! 명령과 쿼리 분리하기 14:43 ~해당 부분에서 Player의 move 메서드를 canMove라는 쿼리와 move라는 명령으로 분리했는데, 여기서 궁금한 점이 있습니다. Player는 이동 가능 여부에 대해 스스로 판단하고 그 결과에 따라 상태를 스스로 변경하는데, Game의 tryMove에서 성공과 실패에 대해 try-catch 구문을 사용하지 않고 if 분기를 사용하는 이유가 무엇인가요?저는 tryMove에서 canMove 쿼리 호출 없이 move 명령을 호출하고, 성공/실패에 대한 처리는 try-catch에서 예외처리하는 것이 더 간단하다 생각했습니다.이번 강의인 6-2의 Player와 8-2 아이템 이동 로직 개선 강의의 Transfer 객체 둘 다 동일한 구조로 코드가 짜여져있어 if 분기를 사용한 이유가 궁금합니다!현재 구조에서 Player의 move 메서드에 예외를 던지는 if 분기가 없다면 Game이 Player의 이동에 관련된 모든 것을 결정하며 TDA를 위반하는 코드라 생각합니다.그렇다면 if 분기는 TDA를 위반하지 않기 위해 넣은 것인가요? 아니면 Player 와 협력하는 다른 클라이언트가 canMove 호출 없이 move를 호출할 경우를 대비해 넣은 것인가요? 후자라고 생각하기에는 1번 질문처럼 Player가 스스로 판단할텐데 try-catch 대신 if 분기를 넣은게 잘 와닿지 않아 질문드립니다!
-
해결됨오브젝트 - 설계 원칙편
9-1 사소한 강의자료 오류
9-1. 더 많은 요구사항 추가하기강의자료 p.45, 강의영상 9:05 ~WorldMap은 target 으로 사용될 경우에만 Carrier를 치환할 수 있는데, 해당 슬라이드에서만 강의자료의 설명과 X 표시가 잘못된 것 같습니다. 영상에서의 설명은 올바른데 자료 표기만 문제가 있는 것 같아 남깁니다!
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
조회 시 엔티티를 조회하는 것이 아닌QueryDSL을 많이 사용 시 Dto는 어떻게 하나요?
일반적인 JpaRepository를 사용할 경우Entity -> Domain model 객체로 변환하는데보통 상세 조회, 목록 조회 시에는 QueryDSL를 사용하고, 또한 조회 시 Projections을 이용하여 Dto로 반환하는데요. 이 때, 조회된 Dto는 User와 마찬가지로 UserDetail, UserList(목록 Dto 명칭이 마땅히 생각나질 않네요.) 와 같은 도메인 모델을 추가로 만들어서 사용하는 건가요? 위와 같은 질문을 드리는 이유는 만약 User가 Team라는 도메인과 연관 관계가 있을 때 Team 정보를 포함하여 응답할 경우 User 따로 조회, Team 따로 조회 하여 응답 dto로 변환해야 할 것같은데 그럼 성능적인 이슈가 생기지 않을까 해서 질문드립니다. (위 예시는 간단해서 성능적 이슈는 크게 없지만 복잡한 도메인에서는 여러 도메인의 정보를 조회해와야 하기 때문에 충분히 성능적 이슈가 있을걸로 판단됩니다.)
-
해결됨오브젝트 - 설계 원칙편
7-3 상속을 이용한 중복 제거 질문 있습니다!
7-3. 의존성 역전 원칙 - 추상화와 세부사항 13:43~수업을 듣던 도중 추상 클래스를 인터페이스와 함께 사용한 이유가 궁금해 질문드립니다.상속이 코드 재사용을 위해 그다지 좋은 방법이 아니라는(+ 자세한 내용은 뒤에서 더 자세히 살펴봄) 언급을 해주셨지만, 우선 여기서는 왜 이렇게 코드를 짰는지 궁금해 고민한 부분을 여쭤보고자 합니다. 인터페이스와 추상 클래스를 함께 사용하는 이유 1. 협력을 위해 제공하는 메시지를 확인하기 용이하기 때문인가요?해당 강의를 듣고나서 맨 처음 들었던 생각은, "추상 클래스로만 추상화를 한 뒤, 상위 수준 객체가 인터페이스 대신 추상 클래스를 의존하면 안될까?" 였습니다. 추상클래스를 사용하더라도 read()만 public이고 readLines()나 parse() 는 각각 private, protected 이므로 외부에 노출되지 않아서 괜찮지 않을까 생각했습니다.더 고민해 보았는데, 인터페이스를 사용한다면 코드를 유지보수하는 과정에서 다음과 같은 이점을 얻을 수 있어서 그런가? 라는 생각이 들어 질문드립니다. 상위 수준의 객체가 인터페이스에 의존하면 역할을 한 눈에 파악하기 쉽다.Reader 는 read()라는 메서드 시그니처로 "특정 데이터소스로부터 읽어오는 작업"을 수행한다. 라는 것을 인터페이스를 통해 명시한다. 즉, "명세" 역할을 한다.이는 유지보수 과정에서 해당 인터페이스만 읽고 구현체가 제공하는(또는 해야하는) 기능(public 메서드)들을 확인하기 용이하다. 즉, 역할을 한 눈에 파악하기 쉽다. 추상 클래스에 의존하면 역할을 한 눈에 파악하기 어렵다.AbstractReader는 메서드 시그니처 뿐만 아니라 중복 로직의 경우 구현 내용까지 포함하고 있고, 여러 메서드들 중 외부에서 협력하기 위해 pubilc으로 노출시켜 제공하는 기능을 한 눈에 파악하기 어렵다.위 두 가지 내용이 인터페이스를 추상클래스와 함께 사용하는 이유가 될 수 있을까요? 2. 두 public 메서드 중 하나는 중복 로직, 하나는 각 구현체마다 다르게 구현하는 경우에도 인터페이스와 추상클래스를 함께 사용하는 것이 좋을까요?1번에서 인터페이스와 추상클래스를 함께 사용한 이유로 언급했던 "역할을 한 눈에 파악하기 쉽다"는 장점이 있다면, 두 메서드 모두 public일 때에도 추상클래스로만 구현하기 보다는 인터페이스와 추상클래스를 함께 사용하는 것이 좋을까요? 2번 질문은 예시 코드를 드리자면 Spring Boot 개발환경에서 작업한 코드로 CaptchaHashProcessor 인터페이스에는 public 메서드인 hash() , verify() 두 메서드가 있습니다. 해당 인터페이스를 구현한 두 구현체에서 verify() 메서드가 중복되는 상황입니다. 기존 코드두 public 메서드를 제공하는 인터페이스public interface CaptchaHashProcessor { HashResult hash(Long captchaId); Long verify(String hashedCode, Long userId); }구현체 1 - hash는 다르게 구현하나 verify는 구현체 2와 내용 동일public class RandomCaptchaHashProcessor implements CaptchaHashProcessor { private static final SecureRandom RANDOM = new SecureRandom(); private final Encryption encryption; private final CaptchaLogPort captchaLogPort; private final EncryptionProperties properties; @Override public HashResult hash(Long captchaId) { // 구현체마다 다름... } @Override public Long verify(String encryptedCode, Long userId) { // 중복 로직 ... } }구현체 2- hash는 다르게 구현하나 verify는 구현체 1과 내용 동일public class FixedCaptchaHashProcessor implements CaptchaHashProcessor { private static final String FIXED_IV = Base64.getEncoder().encodeToString(new byte[16]); private final Encryption encryption; private final CaptchaLogPort captchaLogPort; @Override public HashResult hash(Long captchaId) { // 구현체마다 다름... } @Override public Long verify(String hashedCode, Long userId) { // 중복 로직 ... } } 추상클래스 사용여기서 추상클래스로 verify 중복 로직을 이동시키면서 인터페이스를 사용한다면인터페이스를 구현한 추상클래스 public abstract class AbstractCaptchaHashProcessor implements CaptchaHashProcessor { protected final Encryption encryption; private final CaptchaLogPort captchaLogPort; @Override public Long verify(String hashedCode, Long userId) { // 추상 클래스로 이동한 중복 로직 ... } }구현체 1public class RandomCaptchaHashProcessor extends AbstractCaptchaHashProcessor { private static final SecureRandom RANDOM = new SecureRandom(); private final EncryptionProperties properties; public RandomCaptchaHashProcessor(Encryption encryption, CaptchaLogPort captchaLogPort, EncryptionProperties properties) { super(encryption, captchaLogPort); this.properties = properties; } @Override public HashResult hash(Long captchaId) { // 구현체마다 다름 ... } }구현체 2public class FixedCaptchaHashProcessor extends AbstractCaptchaHashProcessor { private static final String FIXED_IV = Base64.getEncoder().encodeToString(new byte[16]); public FixedCaptchaHashProcessor(Encryption encryption, CaptchaLogPort captchaLogPort) { super(encryption, captchaLogPort); } @Override public HashResult hash(Long captchaId) { // 구현체마다 다름 ... } }이렇게 구현할 수 있을텐데, 인터페이스를 사용하지 않는다면 hash 메서드까지 추상 클래스의 추상 메서드로 명시해서 상위 수준 클래스가 추상 클래스에 의존해도 될 것 같아서 고민이 됩니다! 이 질문을 작성하면서 다른 생각도 떠올랐는데요, CaptchaHashProcessor는 캡챠 코드를 암호화(hash)하고 캡챠 코드를 검증(verify)한다는 두 가지 책임을 가진 것 같아서 어쩌면 암호화 책임은 인터페이스와 그 구현체들로 제공하고, 검증 책임은 또다른 클래스에서 구현하는 것이 적절한가? 하는 생각도 듭니다... 좋은 강의를 제공해주시고 긴 질문 읽어주셔서 감사합니다.질문은 타인이 저에게 소중한 시간을 소비하는 것이라 생각해 강사님의 시간 낭비가 되지 않도록 영양가 있는 질문을 잘 하고싶습니다. 혹시나 질문의 내용 구성이나 태도, 질문을 이끌어낸 사고과정 등에서 부족한 부분이 보였다면 어떻게 개선하면 좋을지 말씀해주시면 감사하겠습니다!
-
미해결Flutter 테스트 기초
Mockito 강의 Exception
Mockito강의를 듣고 있는데요첨부된 자료 다운로드 후 실행을 했는데api 요청에서 Exception이 발생하고 있습니다.어떻게 해결해야하나요?
-
미해결쉬운 모바일 테스트 자동화 시작하기 : Appium Studio
Appium & Android Studio 설치 관련
안녕하세요 선생님 이번에 강의를 처음 듣게 되었는데요 자료에 남겨주신 Appium 설치 파일이 다운로드가 되지 않아 혹시 해당 강의를 따라할 수 있는 Appium이나 Android Studio를 다운로드 가능한 파일이나 참고할 설치 가이드가 있는지 궁금합니다~
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
controller 의 port 패키지의 service 인터페이스 사용
controller 패키지가 service 패키지에 의존해야하는데 interface를 controller의 port에 둠으로써 service가 controller에 의존하게 된다고 말씀하시고 수정한다고 했는데 수정이 없네요. service 인터페이스들을 service.port 패키지로 옮겨주면 될거 같습니다.
-
해결됨Practical Testing: 실용적인 테스트 가이드
고전파의 테스트 대역 사용 대상, 공유 의존성
안녕하세요복습을 진행하면서 단위 테스트(블라디미르 코리코프)를 같이 공부하는데, gpt와 씨름해 보아도 모르겠어서 질문 드립니다 ㅠㅠ책에 따르면 고전파의 테스트 대역 사용 대상은 공유 의존성으로 유일하고, 이것의 예로 데이터베이스를 들고 있는데요.우빈님의 강의에 따르면 이것은 고전파의 방식과는 거리가 멀어 보여서 혼란이 옵니다테스트 대역을 쓰고 싶다면, 공유 의존성(데이터베이스)은 가능하다라는 뜻 인걸까요?만약 그렇다면, 고전파가 테스트 대역 사용에 엄격한 방식이라고 이해했었는데, 데이터베이스를 유일한 모킹 가능성 영역이라고 보는 것이 납득하기 어렵습니다강의에서 가르쳐주신 것처럼 외부 서비스(메일)을 모킹 처리 하는 것이 더 나은 방식, 혹은 고전파 다운 방식이라고 생각되어서 혼란스럽습니다..
-
미해결Java/Spring 테스트를 추가하고 싶은 개발자들의 오답노트
빌드 문제
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!-- 1) 여기만 3.3.0(또는 3.2.5 등)으로 변경 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>demo</description> <properties> <java.version>21</java.version> <lombok.version>1.18.36</lombok.version> </properties> <dependencies> <!-- Spring Boot Starters --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- 기타 의존성 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>6.0.3</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> <!-- Jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.14.1</version> </dependency> <!-- Springdoc --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.0.2</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.14</version> </dependency> <!-- Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- (선택) JDK 툴체인 강제 설정 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-toolchains-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <goals> <goal>toolchain</goal> </goals> </execution> </executions> <configuration> <toolchains> <jdk> <version>${java.version}</version> </jdk> </toolchains> </configuration> </plugin> <!-- 자바 21 + Lombok 어노테이션 프로세서 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <release>${java.version}</release> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> </annotationProcessorPaths> <fork>true</fork> </configuration> </plugin> <!-- Spring Boot Maven Plugin: Lombok 제외 유지 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
-
해결됨오브젝트 - 설계 원칙편
함수형 스타일에 대해선 어떻게 생각하시나요?
이제 꼭 함수형 언어를 사용하지 않더라도 요즘 언어들 대부분 함수형 언어의 아이디어나 장점들을 반영하는 트렌드인데, 회사에서도 코틀린을 사용 중이라 질문 드려봅니다.코틀린 라이브러리들을 사용하다 보면 함수형 스타일의 설계와 API들을 꽤 많이 보고 있기도 하고 특히 람다를 굉장히 많이 사용하는데 적절하게 람다나 함수형 스타일의 코드를 사용하는 기준이나 원칙들도 있으신지 궁금하네요.
-
해결됨오브젝트 - 설계 원칙편
값 객체 활용에 대해
안녕하세요. 항상 객체 지향의 본질을 가르쳐주시는 강의 정말 잘 듣고 있습니다. 실무에서 JPA를 사용할 때 값 객체 활용에 대해서도 궁금합니다. RDB 기준 하나의 테이블에 매핑되는 경우말고, 값 객체를 별도의 테이블로 매핑해야될 경우에도 많이 사용하시는지 궁금합니다.