묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
JPA 를 사용하면 원래 객체지지향적인 설계를 가져가기 어렵나요?
오랜만에 인사드립니다 강사님. 저는 수강한 강의내용을 토대로 현재 진행하고있는 프로젝트를 리팩토링하고 있습니다. 본론으로 들어가자면, 일단 아래와 같이 특정 비지니스 로직을 검증하는 코드가 있습니다.======== BEFORE =========private void checkCrewInfoIsMatch(Long requestCrewId, Squad squad) { if (!squad.getCrew().getId().equals(requestCrewId)) { throw new SquadBusinessException.NotMatchCrewInfo(NOTMATCH_CREWINFO); } } private void checkDifferenceSquadCreator(Squad squad, CrewMember crewMember) { if (squad.getCrewMember().equals(crewMember)) { throw new SquadBusinessException.OwnerCantParticipant(OWNER_CANT_PARTICIPANT); } } 그리고 위 코드를 리팩토링하고나서 아래와 같은 코드가 나오게 되었습니다. ========== AFTER =========== private void checkCrewInfoIsMatch(Long requestCrewId, Squad squad) { if (squad.hasNotSameCrewId(requestCrewId)) { throw new SquadBusinessException.NotMatchCrewInfo(NOTMATCH_CREWINFO); } } private void checkDifferenceSquadCreator(Squad squad, CrewMember crewMember) { if (squad.isSameOwner(crewMember)) { throw new SquadBusinessException.OwnerCantParticipant(OWNER_CANT_PARTICIPANT); } } 이렇게 Getter 로 값을 꺼내 직접 값을 비교했던 (Before) 에서, 값의 비교를 객체에게 위임하는 형태인 (After) 로 리팩통해보았습니다. 하지만 값의 비교를 객체에게 위임하는 After 방식으로 리팩토링했더니, 엔티티와 연관된 다른 프록시 객체들의 초기화로 인해 불필요한 쿼리가 나가는 문제를 확인하게 되었습니다. 물론 fetchJoin 을 통해 해결하긴 했습니다. 정리하면 아래와 같습니다. 기존에는 Service Layer 에서 직접 특정 엔티티와 연관된 엔티티를 직접 Getter 로 꺼내 값을 비교했었고, N + 1 이 터지지 않았습니다. (Before) 하지만 남용된 Getter 를 개선하기 위해 값의 비교를 엔티티 내부에서 진행하는 방식으로 리팩토링했을때는 N + 1 이 터졌고, 이를 fetchJoin 으로 해결했습니다. (After) 물론 지금은 fetchJoin 으로 N + 1 문제를 해결하였지만, After 와 같은 방식으로 리팩토링을 하려면 "연관된 엔티티를 모두 fetchJoin 미리 땡겨와야하는건가? (column 들을 너무 많이 땡겨오면 성능문제 걱정)" 라는 생각이 들게되었습니다. JPA 를 사용할때, 강사님은 어떠한 방식으로 연관된 엔티티들의 값 비교를 어떤 방식으로 진행하는지 매우 궁금 합니다. 원래 JPA 를 사용하면 객체지향적인 설계를 가져가기 어려운건지도 궁금합니다. (깃헙에서 다른 사람이 한 프로젝트를 분석하다보면, 가끔 연관관계 자체를 맺지 않고, FK 를 Long 으로 가져가버리는 Case 도 본것 같은데 흠.. 나중에 질문)
-
해결됨Readable Code: 읽기 좋은 코드를 작성하는 사고법
강의 배운 거를 프로젝트에 적용하고 있는데 궁금한 게 있어요!
public class MetricService { // 생략 private Map<String, Double> parseNetworkUsage(String line) { Map<String, Double> networkUsageMap = new HashMap<>(); Matcher matcher = NETWORK_PATTERN.matcher(line); if (matcher.find()) { String[] parts = line.split("\\s+"); if (parts.length >= 10) { long receivedBytes = parseLongSafely(parts[RECEIVED_BYTES_INDEX]); // 수신된 바이트 long transmittedBytes = parseLongSafely(parts[TRANSMITTED_BYTES_INDEX]); // 송신된 바이트 networkUsageMap.put(METRIC_MAP_NETWORK_REC, convertBytesToMB(receivedBytes)); networkUsageMap.put(METRIC_MAP_NETWORK_SENT, convertBytesToMB(transmittedBytes)); } } return networkUsageMap; } private double convertBytesToMB(long bytes) { return bytes / BYTES_TO_MB_DIVISOR; } private long parseLongSafely(String value) { try { return Long.parseLong(value); } catch (NumberFormatException e) { return 0L; } } }위는 MetricService 클래스고, 여기에 명령어 결과값을 파라미터로 받아서 네트워크 사용량을 파싱하는 parseNetworkUsage()가 있어요. 그리고 가독성을 위해 추출한 유틸성 메서드인 convertBytesToMB과 parseLongSafely 메서드가 있습니다. 제가 궁금한 거는 convertBytesToMB, parseLongSafely와 같은 유틸성 메서드는 MetricService 클래스 안에 둬야할지 아니면 무조건 유틸 클래스로 빼야하는지 궁금합니다! 재활용이 여러 번 되면 뺄 거 같은데.. 단 한 번만 사용이 되서 유틸 클래스로 빼기도 애매하고, 그렇다고 MetricService 클래스와는 관심사가 다르다고 생각해서 여기둬도 괜찮을까라는 생각이 자꾸 드네요.
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
입출력 테스트 관련 질문 있습니다!
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 안녕하세요. 우선 강의로 너무 많은 도움을 받았습니다! 감사합니다. 다름이 아니라 로드맵을 따라 해당 강의와 테스트 코드 작성 강의를 모두 수강했는데 현재 지뢰찾기와 스터디카페에서는 콘솔에서 입출력을 받게 되는데 입출력에 대한 예외처리를 InputHandler가 하는 것처럼 다가왔습니다.이부분에서 테스트를 작성하려고 하는데 입출력에 대한 테스트를 작성하기 어려워서 과연 InputHander가 입력에 대한 검증 책임까지 가지고 있는가에 대해서 궁금하고 만약 분리해야 한다면 어떻게 분리하는 것이 좋을 지도 궁금합니다. 또한 현재의 경우 콘솔로 입출력을 받아 테스트가 어렵게 다가오는데 이런 경우이때 만약 전체 서비스가 외부에서 유저의 입력을 문자열을 받는 형태로 분리해야하는데 이 경우에 외부에서도 입출력을 하고, 내부에서도 입출력을 하게 되어 어떻게 하면 좋을지 고민이 되고혹은 현재 그대로 콘솔로 테스트 하는 것이 좋을지 궁금합니다. 콘솔에서 입출력을 받을 때 테스트 코드를 작성하는 방법도 알 수 있을까요?? 그리고 테스트 강의의 테스트 환경의 독립성을 보장하자 강의에서 테스트 코드에서는 객체를 생성할 때 생성자로 생성해서 테스트해야한다고 말씀을 하셨는데 만약 특정 필드가 기본값을 가져야 해서 생성자를 private로 막은 후 정적 팩토리 메서드를 이용해 생성된다면 이러한 경우 기본값을 가지는 특정 필드를 어떻게 특정 값으로 세팅해서 테스트 할 수 있을지도 궁금합니다. 이런 경우 생성자, getter 같은 테스트만을 위한 코드를 넣어도 된다고 하셨는데 이런 경우 원하는 객체의 불변성이 깨지는 것 같아서 어떻게 하면 좋을지 궁금합니다. 감사합니다
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
지역변수 인라인화
안녕하세요 우빈님! 강의 너무 잘 보고 있습니다:) 강의를 보다가 사소한 부분이지만 궁금증이 생겨 여쭤봅니다! 강의 시간 30:58초쯤 위 사진처럼 findCell을 지역변수로 뽑아내셨는데 이렇게 하면 가독성 측면에서 유리한가요?! 지역변수를 인라인화 시키는게 좋다고 들은것 같아서 여쭤봅니다!
-
해결됨Readable Code: 읽기 좋은 코드를 작성하는 사고법
질문 제목을 뭐라 적어야할지 모르겠습니다. 죄송합니다
우선 강의 너무 재밌게 잘 보고있단 말씀 드리고 싶습니다. 일단 새로운 도메인에서 혼자 리팩토링을 하고있었는데요, List<StudyCafeLockerPass> 를 일급 컬렉션으로 감싸 StudyCafeLockerPasses 를 만들었습니다. 여기서 StudyCafeLockerPass 에게 type 과 duration 이 같은지 비교하는 질문을던질 때 StudyCafePass 자체를 인자로 넘길지, 아니면 StudyCafePass 로부터 type 과 duration 을 꺼내서 넘길지 고민입니다. public class StudyCafeLockerPasses { private final List<StudyCafeLockerPass> lockerPasses; public StudyCafeLockerPasses(List<StudyCafeLockerPass> lockerPasses) { this.lockerPasses = lockerPasses; } public static StudyCafeLockerPasses of(List<StudyCafeLockerPass> lockerPasses) { return new StudyCafeLockerPasses(lockerPasses); } // TODO V1. 여기 (StudyCafeLockerPass 에게 질문을 던질때 StudyCafePass 자체를 넘길지) public StudyCafeLockerPass findOneBy(StudyCafePass selectedPass) { return lockerPasses.stream() .filter(option -> option.isEqualWith(selectedPass)) .findFirst() .orElse(null); } // TODO V2. 여기 (StudyCafeLockerPass 에게 질문을 던질때 StudyCafePass 에서 type 와 duration 을 getter 로 꺼내서 넘길지) public StudyCafeLockerPass findOneBy2(StudyCafePass selectedPass) { return lockerPasses.stream() .filter(option -> option.isEqualWithV2(selectedPass.getPassType(), selectedPass.getDuration())) .findFirst() .orElse(null); } } V1 같은 경우의 StudyCafeLockerPass 메서드는 아래와 같습니다.public class StudyCafeLockerPass { private final StudyCafePassType passType; private final int duration; private final int price; public boolean isEqualWith(StudyCafePass studyCafePass) { return isSamePassType(studyCafePass.getPassType()) && isSameDuration(studyCafePass.getDuration()); } public boolean isSamePassType(StudyCafePassType passType) { return this.passType == passType; } public boolean isSameDuration(int duration) { return this.duration == duration; } } V2 경우의 StudyCafeLockerPass 메서드는 아래와 같습니다.public class StudyCafeLockerPass { private final StudyCafePassType passType; private final int duration; private final int price; public boolean isEqualWithV2(StudyCafePassType passType, int duration) { return isSamePassType(passType) && isSameDuration(duration); } public boolean isSamePassType(StudyCafePassType passType) { return this.passType == passType; } public boolean isSameDuration(int duration) { return this.duration == duration; } }제가 느끼기에는 Getter 를 사용하지 않으려면 V1 이 맞는거같고.., 의존성을 생각한다면 V2 가 맞는거같은데 강사님의 기준이 있으실까요 (질문이 제가 봐도 이상한것같은데.. 죄송합니다)
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
정적 팩토리 메서드의 위치는 어딜까요?
안녕하세요 강사님. 강의 잘듣고있습니다. 다름이 아니라 정적 팩토리 메서드는 어디쯤 위치하느게 좋을까요? 우테코 컨벤션이나, 구글 컨벤션을 보면, public, private, ... static.. etc.. 순으로 정의하라고 나와있는데요 생성자에 의미를 부여하는 팩토리 메서드 public static 은 어디쯤 두는게 좋을까요? 컨벤션상으로는 하단이 맞는데.. 저는 개인적으로 생성자 바로 밑에 둬야할 것 같아요. 강사님 의견은 어떠실까요?
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
단축키 질문드립니다
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 14분쯤 getSelectedRowIndex 메서드 추출하실때두 줄을 한번에 선택하시는데 단축키가 뭘까요..
-
해결됨Readable Code: 읽기 좋은 코드를 작성하는 사고법
리팩토링과 기능 추가에 대한 질문
게임의 난이도를 추가하는 부분에서 행과 열의 사이즈가 상수로 고정되어 있기 때문에 확장에 닫혀있어 OCP가 충족되지 않는 상태라고 하셨는데기능 추가와 리팩토링에 대해 질문이 있습니다. 1) GameLevel인터페이스와 각각의 난이도별 클래스를 작성하는 과정은 OCP를 만족하는 코드로 바꾸는 과정이니까 '리팩토링으로 기능 추가를 용이하게 하는 과정'이라고 봐야하나요? 2) 사용자에게 난이도를 선택할수 있게 물어보는 부분을 작성한다면 그 과정을 기능 추가로 보고 그 이전 단계까지는 리팩토링인건가요? 3) 보통 리팩토링과 기능 추가가 자연스레 같이 이뤄지는 경우에는 커밋을 분리하는지 한 번에 하는지도 궁금합니다.
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
강의에서 발생한 이슈에 관하여 (30:40)
안녕하세요. 30:40 강의 구간에서 발생한, 메서드 추출 이슈에 관하여, 원인이 무엇인지 궁금해서 질문 드립니다.발생한 문제는지뢰를 밟았을 때, 모든 지뢰 구간이 노출 된다거나,셀을 열었을 때 엉뚱한 곳도 같이 열리는 현상이 나타납니다.문제 발생 지점은 updateCellAt 메서드 사용 구간 입니다. private void initializeEmptyCells(CellPositions cellPositions) { List<CellPosition> positionList = cellPositions.getPositionList(); updateCellsAt(positionList, new EmptyCell()); } private void initializeLandMineCells(List<CellPosition> landMinePositionList) { updateCellsAt(landMinePositionList, new LandMineCell()); } ... private void updateCellsAt(List<CellPosition> positionList, Cell cell) { for (CellPosition position : positionList) { updateCellAt(position, cell); } } 상위 호출 부분으로 넘어가서,제 생각에 initializeEmptyCells 와 initializeLandMineCells 메서드 호출로 넘기는 인자가, 공유될 대상이었나 라고 생각해볼 수 있었습니다.cellPositions 나 landMinePositionList 모두 새로운 컬렉션으로 리턴되어서, 공유되지 않다고 생각되었습니다. public void initializeGame() { CellPositions cellPositions = CellPositions.from(board); initializeEmptyCells(cellPositions); List<CellPosition> landMinePositionList = cellPositions.extractRandomPositions(landMineCount); initializeLandMineCells(landMinePositionList); ... }그러면 어느 부분이 메서드 추출로 인해, 공유될 대상이 발생한 원인이었나가 궁금합니다.
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
선생님의 인텔리제이 설정에서 질문이 있습니다
안녕하세요 워밍업 클럽 잘 듣고 있습니다수업이랑 크게 관게 없는 질문인데 강의를 보다보면 코드 라인 옆에 커밋 내역이랑 사용자가회색 글씨로 나오더라구요어떻게 하는 건 지 궁금합니다 감사합니다
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
toEntity
안녕하세요 선생님 강의 잘 보고 있습니다. 강의 내용과는 좀 관련이 없는 개인적인 질문인데 dto -> entity / entity ->dto 로 변환할때 선생님은 어떻게 풀어서 하시나요(mapper ,builder ..etc)? 물론 팀마다 컨벤션이 있고 뭐가 최고라고 말할 수는 없지만 정말 개인적으로 궁금해서 질문 드립니다! 감사합니다
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
CellSnapshotStatus 새 타입 추가로 인한 CellSignProvider 대응이 필요하다는 걸 컴파일 타임에 알 수 있을까요?
안녕하세요! 강의 잘 듣고 있습니다!그리고 '다형성 활용하기' 챕터에서 enum의 interface 구현에 대해 새롭게 알게 되어 좋았습니다!그런데, 궁금한 점이 생겨 이렇게 문의하게 되었습니다.바로 CellSnapshotStatus enum과 CellSignProvidable 인터페이스를 구현한 CellSignProvider enum 사이의 관계입니다. CellSnapshotStatus에 새로운 타입(e.g. STAR)을 추가하게 되었을 때, 개발자가 CellSignProvider에 대해서도 알고 있어야 CellSignProvider에도 새로운 타입(STAR)에 대한 대응을 할 수 있을 것 같은데요CellSnapshotStatus에 새로운 타입이 추가되면 CellSignProvider에도 이에 대한 대응이 필요하다는 것을 컴파일 타임에 알 수 있는 방법이 있을까요?
-
해결됨Readable Code: 읽기 좋은 코드를 작성하는 사고법
정적 팩토리 메서드 of() 컨벤션
학습 관련 질문을 남겨주세요. 어떤 부분이 고민인지, 무엇이 문제인지 상세히 작성하면 더 좋아요!먼저 유사한 질문이 있었는지 검색해 보세요.서로 예의를 지키며 존중하는 문화를 만들어가요. 강의 중 정적 팩토리 메서드를 만드실 때 of()안의 파라미터가 없거나, 한 개 혹은 여러 개 일 때 모두 사용 하신 것 같아요유명 기술 블로그를 보면 https://tecoble.techcourse.co.kr/post/2020-05-26-static-factory-method/여러개의 파라미터를 통해 생성할 때 of를 쓰라고 되어있는데요, 이때 여러개 라는 말은 없거나, 한개 혹은 여러개 모든 경우를 포함하는 말 일까요?실제로 List인터페이스의 경우에서도 구분하지 않고 전부 of를 사용해서 맞는 것 같긴 한데 위의 블로그 표현과는 약간 다른 것 같아서 질문드립니다
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
ocp를 지키기 위해 enum vs interface
안녕하세요. 테스트 강의부터 좋은 강의 감사합니다.강의를 보던 도중에 궁금한 점이 생겨서 질문드립니다.OCP를 만족하는 GameLevel을 만들기 위해 enum도 비슷한 역할을 할 수 있는데 interface를 사용한 이유가 궁금합니다 !@Getter public enum GameLevel { BEGINNER(...), MIDDLE(...) private final int rowSize; private final int colSize; private final int landMineCount; }enum으로 구현 시 새로운 레벨의 추가가 GameLevel의 변화를 가져오기 때문인지..혹은 이를 구분하는 다른 기준이 있을지 궁금합니다.
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
의도한 예외와 의도하지않은 예외가 정확하게 무슨뜻인가요 ?
우선 강의 잘 듣고있습니다. 의도한 예외와 의도하지 않은 예외가 조금 헷갈리는데 제가 이해한 개념이 맞는지 여쭈어봅니다. 강사님께서 말씀하시는 의도한 예외는 개발자가 인지하고 Exception을 던지는 경우를 말씀하시고 의도하지 않은 예외는 개발자가 인지하지 못하고 Exception이 발생한다는 뜻일까요 ?? 그렇다면 잘짜여진 코드에서는 모두 의도한 예외가 되어야 되는건가요 ?? 의도하지 않은 예외를 사용하는 일은 없는건가요 ? 의도한 예외 - 개발자가 예상하고 처리한 예외의도하지 않은 예외 - 개발자가 놓치거나, 빼먹은 예외의도하지 않은 예외 발생시 의도한 예외로 바꾼다.이렇게 이해하면 될까요 ?
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
안녕하세요 private메소드에 대해서 질문 있습니다.
제가 헷갈려서 질문드려요!private 메소드를 만들게 된다면 무언가 책임이 이상하거나 설계가 잘못되었다는 신호로 알고 있는데요 private static void checkIfGameIsOver() { boolean isAllOpened = isAllCellOpened(); if (isAllOpened) { gameStatus = 1; }}만약 이런 메서드를 테스트 한다고 하면 어떻게 해야하는건가요?! private메소드를 현업에서도 사용하시는지 궁금합니다.그리고 제가 어디를 잘못알고 있는지도 궁금해요!
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
추상화 레벨
안녕하세요, 우빈님! 지금까지 열심히 따라오고 있는 학습자 1인입니다! 다름이 아니라 궁금한게 생겨서 질문을 하게 되었습니다. 추상화 과정을 거치면서 변수명도 변경하고 메서드 분리작업 및 추상화 레벨을 맞추는 실습을 하면서 읽기 좋은 코드 과정을 거치고 있는데 여기서 저는 주석을 이용하면 더 깔끔해지게 읽을수 있을것 같다는 생각이 있었습니다! 이 부분에 대해 강사님 생각을 듣고 싶습니다!강사님이 강의 중간중간 추상화 시켜야할 냄새가 난다는 표현을 하셨는데 저는 그 냄새가 잘 안 납니다! 혹시 냄새가 잘 날 수 있는 좋은 방법들이 있을까요? 해당 부분을 많이 해보면 될지 질문드립니다.
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
인프런 워밍업 클럽 커뮤니티 초대장 초대 링크 문제
인프런 워밍업 클럽 커뮤니티 초대장 초대 링크를 받았는데 디스코드에서 만료된 링크라 나오며 디스코드에 참여를 못하고 있습니다.
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
정적 팩토리 메소드 사용 이유
강의를 듣다가 갑자기 드는 질문이 있습니다.생성자를 private로 감추고 정적 팩토리 메소드를 사용해서 of나 from 이런식으로 만드시는데 왜 이렇게 하시는지 궁금합니다.정적 팩토리 메서드는 메서드 이름을 통해 생성의 의도를 명확히 전달할 수 있다고 하는데, 이것말고 또 사용 이유가 있으실까요 ??
-
미해결Readable Code: 읽기 좋은 코드를 작성하는 사고법
게터
안녕하세요 강의 잘 보고 있습니다 게터에 대해 질문이 있어서 그런데 그럼 선생님께서는 실무에서 @Getter은 사용하지 않으시고 필드 하나하나 9분 36초처럼 수동으로 다 만드시나요?? 안쓰자니 코드의 양이 많아지고 쓰자니 코드의 양이 많아져 약간?은 복잡해 질 수도 있고... 감사합니다.