게시글
블로그
전체 82025. 03. 30.
0
[4주차 발자국] 보다 더 나은 테스트를 위해
이번 주차는 시간이 많이 부족했던 관계로, 강의를 통한 느낀점 위주로 작성하겠습니다.인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 강의 내용Presentation Layer기존에 프로젝트에서 Presentation Layer 테스트는 건너뛴 경우가 많았다. 대부분 Business Layer까지만 테스트를 작성했고 사실 어떻게 Presentation Layer 테스트를 작성해야할지 잘 몰라서 안했던 것도 컸다,이번 강의를 통해 MockMVC을 사용하여 Presentation Layer 테스트에 대해 익힐 수 있었고, 프로젝트에도 적용해봐야겠다.Mock을 마주하는 자세Mock은 주로 테스트 하는 대상에 대해 집중하기 위해 사용한다. 예를 들어, 외부 클라이언트에 의존하는 기능이 있을 때 실제 이 기능을 사용하면서까지 테스트할 필요는 없다. 이러한 상황에서 Mock을 사용하게 된다.우리는 주로 테스트를 작성할 때 BDD 스타일로 작성하게 되는데, Mock을 사용하게 되면 given 절에 when().thenReturn()을 사용하게 된다. 이러한 상황에 BDDMockito를 사용하게 되면 given 절에서 given().thenReturn() 형식으로 훨씬 자연스럽게 구성할 수 있다.Mock을 사용하는 관점에서 Classicist와 Mockist가 존재한다. 간단하게 말하면, Classicst는 Mock 사용을 최소하하여 꼭 필요한 경우에만 사용하고, Mockist는 따로따로 다 테스트를 할 수 있기에 보장된 기능은 Mocking 처리를 통해 빠르게 테스트를 한다는 입장이다.두 관점에 대해 확실한 정답은 없다. 소프트웨어의 안정성을 고려하면 Classicst의 입장의 손을 들 수 있다. 프로덕션 코드에서 런타임 시점에 일어날 일을 정확하게 Stubbing 하는 것은 확실하지 않고 모르는 일이다.더 나은 테스트를 작성하기 위한 구체적 조언테스트도 하나의 코드다. 테스트를 작성할 때에도 클린 코드에서 배웠던 내용을 상기시키면서 작성해보자. 또한 테스트 코드에서 테스트하기 어려운 영역이 있다면, 이 것이 리팩토링 또는 영역 분리의 신호는 아닌지 생각하자.또한 @BeforeEach 등으로 테스트마다 중복되는 코드를 분리할 수도 있는데, 중복된다고 모두 분리하는 것은 아니어야 한다. 중복이라고 느껴져서 분리하면 테스트에 대한 이해에 걸림돌이 될 수 있다. 예를 들어, 댓글에 관한 테스트인데 댓글 생성을 @BeforeEach로 분리하면, 테스트를 이해하기에 어려울 수 있다.테스트를 확인하기 위해 모든 테스트를 한꺼번에 돌리면, 스프링이 여러 번 띄워지는 경우를 확인할 수 있다. 이는 테스트 마다 설정이 달라서 그런 것인데, 테스트 환경을 통합하여 스프링 서버가 띄워지는 횟수를 줄여 더 빠르게 테스트하고 확인할 수 있게 하자.private 메서드 테스트는 어떻게 하나요?private 메서드 테스트는 할 필요는 없고 해서도 안된다.만약 그런걸 느낀다면, 객체를 분리할 시점인지 고민해보자!테스트에서만 필요한 메서드가 생겼는데, 프로덕션 코드에서는 필요없다면..?만들어도 되지만, 보수적으로 접근하자.어떤 객체가 마땅히 가져도 될만한 행위이고, 미래에도 충분히 사용될 수 있는 성격의 메서드 정도는 가능하다.Appendix새로운 라이브러리를 사용하다보면, 기능에 익숙하지 않다. 보통 검색을 통해 해당 라이브러리를 익히거나 하는데 이떄 테스트를 활용할 수 있다. 테스트 코드를 통해 라이브러리에 대한 행동을 정의하고 검증하는 과정을 통해 구체적인 동작과 기능을 학습할 수 있다.테스트 코드로 API 문서를 작성할 수 있다. Spring REST Docs인데, 사실 전에도 많이 들어봤던 이름이다. 하지만 Swagger가 간단하고 편하게 작성할 수 있으므로 자주 사용했는데, Swagger를 사용해보면 프로덕션 코드가 지저분해진다. Presentation Layer에 Swagger 관련 코드가 붙으면서 지저분해지면서 단점을 크게 느낀 경험이 있다. 다음엔 Spring REST Docs를 꼭 활용하자.📌 미션레이어 아키텍처의 테스트영속성 레이어영속성 레이어?DB에 접근하는 계층비즈니스 로직이 들어가지 않은 순수하게 데이터에 대한 처리 및 조회를 수행영속성 레이어의 테스트무엇을 확인해야할까?원하는 데이터에 정확히 접근하는지쿼리가 길어졌을 때, 내가 원하는 데이터에 맞게 작성되었는지어떻게 테스트를 해야할까?영속성 계층이 의존하는 계층이 대부분 상황에서 없기 때문에 단위 테스트 형식으로 진행비즈니스 레이어비즈니스 레이어?비즈니스 로직이 전개되는 계층영속성 레이어가 사용된다.도메인 개념이 적용트랜잭션 개념 적용비즈니스 레이어의 테스트하나의 트랜잭션을 보장하는지 확인비즈니스 로직이 정확히 수행되는지 확인여러 케이스에 대해 테스트하자.프레젠테이션 레이어프레젠테이션 레이어란?외부 세계와 가장 가까운 계층요청과 관련한 데이터를 받는다.요청에 대한 데이터를 전달한다.프레젠테이션 레이어의 테스트요청에서 건너온 값들에 대한 검증도메인 규칙을 제외한 간단한 검증상황에 대한 정확한 응답이 반환되는지 확인@Mock, @MockBean, @Spy, @SpyBean, @InjectMocksMock과 Spy의 차이Mock: 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체Spy: Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체일부는 실제 객체처럼 동작시키고 일부만 Stubbing할 수도 있다.Stub이란?테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체. 그 외에는 응답하지 않는다.여기서 그러면 ‘Mock이랑 Stub이랑 무슨 차이지? 같은 내용같은데?’라는 의문이 들 수 있다.https://inf.run/XaZqkstub은 상태 검증, mock은 행위 검증과 관련되어 있다.public interface MailService { public void send (Message msg); } public class MailServiceStub implements MailService { private List messages = new ArrayList(); public void send (Message msg) { messages.add(msg); } public int numberSent() { return messages.size(); } } // 테스트 public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); } Stub은 메일을 몇 번 보냈는지를 나타내는 상태를 검증한다.// 테스트 public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); Mock mailer = mock(MailService.class); order.setMailer((MailService) mailer.proxy()); mailer.expects(once()).method("send"); warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); } } Mock은 행위를 중심으로 검증한다.@Mock, @MockBean@Mock어노테이션이 붙은 객체를 Mock 객체로 생성@MockBean어노테이션이 붙은 객체를 Mock 객체로 생성하고, Spring Context에 등록된 빈을 Mock 객체로 대체한다.@Spy, SpyBean@Spy어노테이션이 붙은 객체를 실제 객체 기반으로 만들지만, 일부 기능만 Stubbing 할 때 사용@SpyBean해당 객체를 Spy 객체로 생성하고, Spring Context에 등록된 빈을 Spy 객체로 대체한다.@InjectMocks@InjectMocks 어노테이션이 붙은 객체가 필요로하는 필드 중 @Mock으로 생성된 객체를 주입해준다.BDD 스타일로 테스트 코드 배치하기아래 3개의 테스트가 있다.@BeforeEach void setUp() { ❓ } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 1-3. 게시물 생성에 필요한 내용 준비 1-4. 게시물 생성 1-5. 댓글 생성에 필요한 내용 준비 1-6. 댓글 생성 // given ❓ // when ❓ // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { 2-1. 사용자 생성에 필요한 내용 준비 2-2. 사용자 생성 2-3. 게시물 생성에 필요한 내용 준비 2-4. 게시물 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 2-7. 댓글 수정 // given ❓ // when ❓ // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { 3-1. 사용자1 생성에 필요한 내용 준비 3-2. 사용자1 생성 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 3-6. 사용자1의 게시물 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 3-9. 사용자2가 사용자1의 댓글 수정 시도 // given ❓ // when ❓ // then 검증 } 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치해야 할까?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)✔ 게시판 게시물에 달리는 댓글을 담당하는 Service Test✔ 댓글을 달기 위해서는 게시물과 사용자가 필요하다.✔ 게시물을 올리기 위해서는 사용자가 필요하다.직접 코드 배치해보기@BeforeEach void setUp() { 사용자 생성에 필요한 내용 준비 사용자 생성 사용자의 게시물 생성에 필요한 내용 준비 사용자의 게시물 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 댓글 생성에 필요한 내용 준비 // when 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 댓글 생성에 필요한 내용 준비 댓글 생성 // when 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 사용자2 생성에 필요한 내용 준비 사용자2 생성 사용자1의 댓글 생성에 필요한 내용 준비 사용자1의 댓글 생성 // when 사용자2가 사용자 1의 댓글 수정 시도 // then 검증 } 공통된 준비 작업을 @BeforeEach로 분리댓글을 달기 위한 조건 작업게시물을 작성할 사용자 생성게시물 생성given검증하고자 하는 행동에 필요한 작업 수행댓글 생성과 관련된 내용은 테스트마다 관리할 수 있도록 given 절에 배치when검증하고자 하는 행동이번 중간 점검에서 Day 18에 대한 공통 피드백이 있었다.핵심은 중복 제거가 아닌 도메인사용자, 게시물은 간접적이므로 setUp()으로, 댓글은 직접적이므로 given절@BeforeEach로의 분리는 중복 제거가 아니라 도메인이다. 중복 제거로 접근하면 좋지 못한 테스트로 이어질 수 있다.
백엔드
・
테스트코드
2025. 03. 27.
0
[인프런 워밍업 스터디] Day 18 미션
인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 @Mock, @MockBean, @Spy, @SpyBean, @InjectMocksMock과 Spy의 차이Mock: 행위에 대한 기대를 명세하고, 그에 따라 동작하도록 만들어진 객체Spy: Stub이면서 호출된 내용을 기록하여 보여줄 수 있는 객체일부는 실제 객체처럼 동작시키고 일부만 Stubbing할 수도 있다.Stub이란?테스트에서 요청한 것에 대해 미리 준비한 결과를 제공하는 객체. 그 외에는 응답하지 않는다.여기서 그러면 ‘Mock이랑 Stub이랑 무슨 차이지? 같은 내용같은데?’라는 의문이 들 수 있다.https://martinfowler.com/articles/mocksArentStubs.htmlstub은 상태 검증, mock은 행위 검증과 관련되어 있다.public interface MailService { public void send (Message msg); } public class MailServiceStub implements MailService { private List messages = new ArrayList(); public void send (Message msg) { messages.add(msg); } public int numberSent() { return messages.size(); } } // 테스트 public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); } Stub은 메일을 몇 번 보냈는지를 나타내는 상태를 검증한다.// 테스트 public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); Mock mailer = mock(MailService.class); order.setMailer((MailService) mailer.proxy()); mailer.expects(once()).method("send"); warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); } } Mock은 행위를 중심으로 검증한다.@Mock, @MockBean@Mock어노테이션이 붙은 객체를 Mock 객체로 생성@MockBean어노테이션이 붙은 객체를 Mock 객체로 생성하고, Spring Context에 등록된 빈을 Mock 객체로 대체한다.@Spy, SpyBean@Spy어노테이션이 붙은 객체를 실제 객체 기반으로 만들지만, 일부 기능만 Stubbing 할 때 사용@SpyBean해당 객체를 Spy 객체로 생성하고, Spring Context에 등록된 빈을 Spy 객체로 대체한다.@InjectMocks@InjectMocks 어노테이션이 붙은 객체가 필요로하는 필드 중 @Mock으로 생성된 객체를 주입해준다.📌 BDD 스타일로 테스트 코드 배치하기아래 3개의 테스트가 있다.@BeforeEach void setUp() { ❓ } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { 1-1. 사용자 생성에 필요한 내용 준비 1-2. 사용자 생성 1-3. 게시물 생성에 필요한 내용 준비 1-4. 게시물 생성 1-5. 댓글 생성에 필요한 내용 준비 1-6. 댓글 생성 // given ❓ // when ❓ // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { 2-1. 사용자 생성에 필요한 내용 준비 2-2. 사용자 생성 2-3. 게시물 생성에 필요한 내용 준비 2-4. 게시물 생성 2-5. 댓글 생성에 필요한 내용 준비 2-6. 댓글 생성 2-7. 댓글 수정 // given ❓ // when ❓ // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { 3-1. 사용자1 생성에 필요한 내용 준비 3-2. 사용자1 생성 3-3. 사용자2 생성에 필요한 내용 준비 3-4. 사용자2 생성 3-5. 사용자1의 게시물 생성에 필요한 내용 준비 3-6. 사용자1의 게시물 생성 3-7. 사용자1의 댓글 생성에 필요한 내용 준비 3-8. 사용자1의 댓글 생성 3-9. 사용자2가 사용자1의 댓글 수정 시도 // given ❓ // when ❓ // then 검증 } 내용을 살펴보고, 각 항목을 @BeforeEach, given절, when절에 배치한다면 어떻게 배치해야 할까?(@BeforeEach에 올라간 내용은 공통 항목으로 합칠 수 있습니다. ex. 1-1과 2-1을 하나로 합쳐서 @BeforeEach에 배치)✔ 게시판 게시물에 달리는 댓글을 담당하는 Service Test✔ 댓글을 달기 위해서는 게시물과 사용자가 필요하다.✔ 게시물을 올리기 위해서는 사용자가 필요하다.직접 코드 배치해보기@BeforeEach void setUp() { 사용자 생성에 필요한 내용 준비 사용자 생성 사용자의 게시물 생성에 필요한 내용 준비 사용자의 게시물 생성 } @DisplayName("사용자가 댓글을 작성할 수 있다.") @Test void writeComment() { // given 댓글 생성에 필요한 내용 준비 // when 댓글 생성 // then 검증 } @DisplayName("사용자가 댓글을 수정할 수 있다.") @Test void updateComment() { // given 댓글 생성에 필요한 내용 준비 댓글 생성 // when 댓글 수정 // then 검증 } @DisplayName("자신이 작성한 댓글이 아니면 수정할 수 없다.") @Test void cannotUpdateCommentWhenUserIsNotWriter() { // given 사용자2 생성에 필요한 내용 준비 사용자2 생성 사용자1의 댓글 생성에 필요한 내용 준비 사용자1의 댓글 생성 // when 사용자2가 사용자 1의 댓글 수정 시도 // then 검증 } 공통된 준비 작업을 @BeforeEach로 분리댓글을 달기 위한 조건 작업게시물을 작성할 사용자 생성게시물 생성given검증하고자 하는 행동에 필요한 작업 수행댓글 생성과 관련된 내용은 테스트마다 관리할 수 있도록 given 절에 배치when검증하고자 하는 행동
백엔드
・
인프런워밍업스터디
・
Mock
・
테스트
2025. 03. 24.
0
Day 16 미션 : 레이어 아키텍쳐의 테스트
이번에 설명하는 레이어는 영속성 레이어, 비즈니스 레이어, 프레젠테이션 레이어를 기준으로 설명하겠습니다.레이어의 특징어떻게 테스트하면 좋을지영속성 레이어영속성 레이어?DB에 접근하는 계층비즈니스 로직이 들어가지 않은 순수하게 데이터에 대한 처리 및 조회를 수행영속성 레이어의 테스트무엇을 확인해야할까?원하는 데이터에 정확히 접근하는지쿼리가 길어졌을 때, 내가 원하는 데이터에 맞게 작성되었는지어떻게 테스트를 해야할까?영속성 계층이 의존하는 계층이 대부분 상황에서 없기 때문에 단위 테스트 형식으로 진행비즈니스 레이어비즈니스 레이어?비즈니스 로직이 전개되는 계층영속성 레이어가 사용된다.도메인 개념이 적용트랜잭션 개념 적용비즈니스 레이어의 테스트하나의 트랜잭션을 보장하는지 확인비즈니스 로직이 정확히 수행되는지 확인여러 케이스에 대해 테스트하자.프레젠테이션 레이어프레젠테이션 레이어란?외부 세계와 가장 가까운 계층요청과 관련한 데이터를 받는다.요청에 대한 데이터를 전달한다.프레젠테이션 레이어의 테스트요청에서 건너온 값들에 대한 검증도메인 규칙을 제외한 간단한 검증상황에 대한 정확한 응답이 반환되는지 확인
백엔드
・
테스트
・
레이어
2025. 03. 23.
0
[3주차 발자국] 스프링과 테스트 코드
인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 3주차 강의Spring & JPA 기반 테스트레이어드 아키텍처와 테스트레이어를 구분하는 이유 ⇒ 관심사의 분리!스프링과 JPA라는 기술 자체가 중요한 것이 아니라,무엇을 어떻게 테스트 할 것인지가 중요하다.통합 테스트여러 모듈이 협력하는 기능을 통합적으로 검증단위 테스트만으로는 기능 전체의 신뢰성을 보장하기 어렵다.풍부한 단위 테스트 & 큰 기능 단위를 검증하는 통합 테스트Spring / JPA 훑어보기 & 기본 엔티티 설계Library vs Framework라이브러리는 내 코드가 주체가 된다.프레임워크에서는 코드를 작성해서 끼워넣는다.SpringIoC : 객체의 생명주기에 대한 관리를 제 3자가 한다.DI : 컨테이너가 주입해준 객체를 사용한다.AOP : 코드 상으로 흩어져있는 부가적인 앞뒤로 해주는 작업을 한 군데로 모은다.JPAORM객체 지향 패러다임과 관계형 DB 패러다임의 불일치를 해결개발자는 단순 작업을 줄이고, 비즈니스 로직에 집중할 수 있다.인터페이스이고, 여러 구현체 중 Hibernate를 많이 사용한다.스프링 진영에서는 JPA를 한번 더 추상화한 Spring Data JPA 제공Persistence Layer 테스트쿼리가 명확한데 굳이 테스트를 작성해야 할까?where 조건이 많아서 쿼리가 길어진다거나, 구현 기술이나 방법이 변경되는 상황에서도 보장할 수 있다.작성한 코드가 제대로 된 쿼리가 날라가는지에 대한 보장미래에 어떤 형태로 변형될지 모르기에 이에 대한 보장Persistence LayerData Access 역할비즈니스 가공 로직이 포함되어서는 안된다.Repository 테스트는 단위 테스트 성격에 가깝다.데이터베이스에 접근하는 로직만 가지고 있기 때문DataJpaTest vs SpringBootTestDataJpaTest는 SpringBootTest보다 가볍다.JPA 관련 빈들만 주입한다.// 리스트 테스트 시 사용하면 좋다! assertThat(products).hasSize(2) .extracting("productNumber", "name", "sellingStatus") .containsExactlyInAnyOrder( tuple("001", "아메리카노", SELLING), tuple("002", "카페라떼", HOLD)); .extracting()검증하고자 하는 필드 추출@ActiveProfiles("test")test 프로파일로 적용Business Layer 테스트Business Layer비즈니스 로직을 구현하는 역할Persistence Layer와의 상호작용을 통해 비즈니스 로직 전개트랜잭션을 보장@DataJpaTest내부에 @Transactional이 존재@SpringBootTest + @Transactional이 조합이 가질 수 있는 문제?프로덕션 코드에 있는 트랜잭션 경계가 모호해질 수 있다.실제로 트랜잭션 설정이 되어있지 않았다면?간단한 것들도 테스트를 해야할까?미래 시점에 대한 대비를 생각하여 작성하자!수량 차감에 대한 비즈니스 로직수량을 차감할 수 있는 조건이 만족하면,수량을 차감한다. (stock.deductQuantity())수량을 차감할 수 있다면,차감한다.수량에 대한 검증 로직을 두 곳에서 다 해야하나?Stock은 비즈니스 로직이 어떻게 되어있는지 모르기 때문에 이에 대한 보장을 해야한다!재고 감소는 동시성 문제를 고려해야 한다. 동시에 같은 재고 개수를 읽으면 문제가 생기기에 보통 lock을 사용한다.Presentation Layer 테스트Presentation Layer외부 세계의 요청을 가장 먼저 받는 계층파라미터에 대한 최소한의 검증을 수행넘겨온 값들에 대한 검증하위 레이어는 Mocking 처리의존관계를 가짜로 처리하여 테스트 대상에 집중💡CQRS란?Command와 Query를 분리하여 서로 연관이 없게 만든다.DB에 대한 엔드포인트를 구분하여 Query용 DB와 Write용 DB를 나누어 쓸 수 있다. (Master/Slave)📌 Day 11 미션 : 단위 테스트 작성하기Day 11 미션은 Readable Code 강의에서 진행한 프로젝트의 테스트 코드를 작성하는 것이다. 테스트에 대해 참고할 조언은 다음과 같다.테스트가 필요하다고 판단하는 과정부터 시작이다.어떤 클래스, 메서드를 테스트하고 싶은지 명확한 이유와 함께 고민해보자.가장 작은 단위의 메서드부터 테스트를 작성해보자.https://github.com/hgh1472/readable-code테스트를 작성해보며 느낀 점은 외부 세계를 분리할수록 테스트를 작성하기 쉽다는 것이다.한가지 예를 들면, 메서드 중 콘솔로 유저 입력을 받고 입력에 해당하는 도메인으로 바꿔주는 메서드가 존재했다. 이 메서드를 테스트하려면 어떻게 해야할까?유저 입력을 System.setIn() 메서드를 통해 미리 준비시키고 테스트를 진행해야 했다. 그런데 InputHandler 내부에서 Scanner를 정적 필드로 가지고 있기 때문에, 한 번에 여러 테스트에 대한 입력이 적용되지 않아서 테스트하는 데에 큰 불편함이 존재했다.즉, 테스트 코드를 작성하면서 프로덕션 코드의 리팩토링까지 생각이 이어질 수 있는 것이다. 테스트 코드는 단순히 기능을 테스트하기 위함 뿐만 아니라, 프로덕션 코드와 상호보완적으로 리팩토링의 포인트가 될 수 있다는 점을 느꼈다!
백엔드
・
테스트코드
2025. 03. 16.
0
[2주차 발자국] Readable Code 적용기
인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 2주차 강의 (Readable Code)코드 다듬기주석의 양면성주석이 많다 ⇒ 비즈니스 요구사항을 코드에 잘 녹이지 못했나 의심해보자.추상화로 설명이 덜 된 것은 아닐까?주석에 의존하면 적절하지 않은 추상화 레벨을 가지게 된다.좋은 주석우리가 가진 모든 표현 방법을 총동원해 코드에 의도를 녹여내고, 그럼에도 불구하고 전달해야 할 정보가 남았을 때 사용하는 주석의사 결정의 히스토리를 도저히 코드로 표현할 수 없을 때, 주석으로 상세하게 설명!번수와 메서드의 나열 순서변수는 사용하는 순서대로 나열하자.인지적 경제성메서드의 순서는 객체의 입장에서 생각해보자.객체는 외부 세계와 어떻게 소통할 것인지가 중요공개 메서드의 스펙을 통해 외부 세계와 소통객체는 협력을 위한 존재외부 세계에 내가 어떤 기능을 제공할 수 있는지를 드러낸다.공개 메서드끼리도 기준을 가져보자.상태 변경 > 판별 > 조회 메서드중요한 것은, 나열 순서로도 의도와 정보를 전달할 수 있다는 것!패키지 나누기패키지는 문맥으로서의 정보를 제공할 수 있다.패키지를 쪼개지 않으면 관리가 어렵다.너무 잘게 쪼개도 안됨대규모 패키지 변경은 팀원과의 합의를 이룬 시점에!기능 유지보수하기객체 지향적으로 책임이 잘 분리되어 있다면,문제가 되는 위치를 발견하여 수정하기 쉽다!알고리즘 교체와 같은 작업이 수월하다!IDE 도움 받기코드 포맷팅sonarlinteditconfig리팩토링 연습스스로 리팩토링을 진행해보고 비교해보는 시간이유를 가지고 리팩토링을 진행하자.변경 포인트는 그 이유를 명확하게 설명할 수 있어야 한다!!감으로 하는 리팩토링은 설득할 수 없는 코드가 될 확률이 높다.객체에 메시지를 보내자.객체의 책임과 응집도를 고려하자.기억하면 좋은 조언들능동적 읽기눈으로 복잡하게 보고 이해하려 애쓰는 것보다 직접 경험해보며 읽자.복잡하거나 엉망인 코드를 읽고 이해하려 할 때, 리팩토링하면서 읽기공백으로 구분메서드와 객체 추상화주석으로 이해한 내용 표기하며 읽기핵심 목표는 도메인 지식을 늘리는 것.그리고 이전 작성자의 의도를 파악하는 것.오버 엔지니어링필요한 적정 수준보다 더 높은 수준의 엔지니어링구현체가 하나인 인터페이스아키텍처 이해에 도움을 주거나, 근시일 내에 구현체가 추가될 가능성이 높다면 괜찮음구현체 수정 시, 인터페이스도 수정코드 탐색에 영향을 준다.너무 이른 추상화정보가 숨겨지기 때문에 복잡도가 높아짐은탄환은 없다만능 해결사 같은 기술은 없다.객체 지향적인 체스 프로그램but, 체스는 500년 동안 변하지 않았다.실무 : 2가지 사이의 줄다리기지속 가능한 소프트웨어의 품질 VS 기술 부채를 안고 가는 빠른 결과물모든 기술과 방법론은 적정 기술의 범위 내에서 사용되어야 한다.도구라는 것은, 한계까지 사용할 줄 아는 사람이 그것을 사용하지 말아야 할 때도 아는 법이다.📌 2주차 강의 (Practical Testing)Intro강의 소개무엇을 학습하는가?테스트 코드가 필요한 이유좋은 테스트 코드란 무엇일까?실무에서 진행하는 방식 그대로 테스트를 작성해가면서 API를 설계하고 개발하는 방법구체적인 이유에 근거한 상세한 테스트 작성 팁어떻게 학습하면 좋을까?무엇을 모르는지 아는 것 = 찾아볼 수 있게 된다는 것선택과 집중을 통해 아는 영역을 넓혀가고, 익숙하지 않은 것들을 익숙하게 하고, 들어보지 못했던 것을 들으면서 나에게 노출시키자.인덱스를 통해 영역을 넓혀가자.테스트는 왜 필요할까?테스트 코드를 작성하지 않는다면,변화가 생기는 순간마다 발생할 수 있는 모든 Case 고려해야 한다.변화가 생기는 순간마다 모든 팀원이 동일한 고민을 해야 한다.빠르게 변화하는 소프트웨어의 안정성 보장 X테스트 코드가 병목이 된다면,프로덕션 코드의 안정성 보장 X테스트 코드 자체가 유지보수하기 어려운 짐잘못된 검증이 이루어질 가능성 O테스트를 통해 얻고자 하는 것 ⇒ 빠른 피드백, 자동화, 안정감따라서, 올바른 테스트 코드는자동화 테스트로 빠른 시간에 버그를 발견하고 비용을 절약한다.소프트웨어의 빠른 변화를 지원한다.팀원들의 집단 지성을 팀 차원의 이익으로 승격시킨다.단위 테스트수동 테스트 vs 자동화된 테스트콘솔에 찍힌 내용을 보고 판단하는 테스트최종 단계에서 사람이 개입해야 함다른 사람이 봤을 때, 무엇을 검증하고 어떤 것이 맞는 상황인지 알 수 없다.과연 이것이 자동화된 테스트일까?JUnit5로 테스트하기단위 테스트작은 코드 단위를 독립적으로 검증하는 테스트작은 코드 = 클래스, 메서드독립적 = 외부 상황에 의존적이지 않아야 한다.검증 속도가 빠르고, 안정적이다.JUnit 5단위 테스트를 위한 테스트 프레임워크AssertJ테스트 코드 작성을 원활하게 돕는 테스트 라이브러리풍부한 API, 메서드 체이닝 지원리스트 사이즈 검증할 때 다음을 이용하자..hasSize().isEmpty()테스트 케이스 세분화하기해피 케이스예외 케이스assertThatThrownBy()경계값 테스트범위, 구간, 날짜 등테스트하기 어려운 영역 분리하기생성 시간 검증에 대한 테스트에서 생성 시간을 메서드 내부에서 측정한다면?테스트하는 시간에 따라 테스트 결과가 달라진다.외부로 분리하자!외부로 분리할수록 테스트 가능한 코드는 많아진다.테스트하기 어려운 영역관측할 때마다 다른 값에 의존하는 코드현재 시간에 의존하는 코드외부 세계에 영향을 주는 코드테스트 하기 좋은 영역 (순수함수)같은 입력에는 항상 같은 결과외부 세상과 단절된 형태TDD : Test Driven Development프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론RED → GREEN → REFACTORRED : 프로덕션 코드 없이 테스트를 먼저 작성GREEN : 테스트가 통과할 수 있는 최소한의 코딩REFACTOR : 테스트 통과를 유지하면서 구현 코드 개선기능 구현 후, 테스트를 작성하면,테스트 자체의 누락 가능성특정 테스트 케이스만 검증할 가능성잘못된 구현을 다소 늦게 발견할 가능성테스트를 먼저 작성하면,복잡도가 낮은, 테스트 가능한 코드로 구현쉽게 발견하기 어려운 엣지 케이스를 놓치지 않게 해준다.구현에 대한 빠른 피드백과감한 리팩토링테스트를 구현부 검증을 위한 보조 수단이 아니라, 테스트와 상호 작용하며 발전하는 프로덕션 코드을 만든다고 바라본다.즉, 객체 사용자 관점에서의 피드백을 준다!추가 인덱스애자일 방법론일정한 주기를 가지고 빠르게 제품을 출시하여 고객의 요구사항, 변화된 환경에 맞게 요구를 더하고 수정해나가는 방법익스트림 프로그래밍빠른 개발 속도를 유지하며 고객이 원하는 요구들을 지속적으로 피드백하는 방법의사소통, 단순성, 용기, 피드백, 존중하나의 방법론 : TDD스크럼, 칸반스크럼 : 스프린트라는 일정기간 안에 완료할 수 있는 작업으로 업무를 분할한다.칸반 : 동시에 개발이 진행될 수 있는 아이템의 수를 제한하여, 업무의 병목 현상과 리소스 낭비를 처리할 수 있도록 한다.테스트는 [ ]다.테스트는 [문서]다.프로덕션 기능을 설명하는 테스트 코드 문서다양한 테스트 케이스를 통해 프로덕션 코드를 이해하는 시각과 관점 보완과거에 경험했던 고민의 결과물을 팀 차원으로 승격시켜, 모두의 자산으로 공유DisplayName을 섬세하게비교음료 1개 추가 테스트음료 1개 추가하면 주문 목록에 담긴다.명사의 나열보다 문장으로테스트 행위에 대한 결과까지 기술하기비교특정 시간 이전에 주문을 생성하면 실패한다.영업 시작 시간 이전에는 주문을 생성할 수 없다.도메인 용어를 사용하여 한층 추상화된 내용을 담기메서드 자체의 관점보다 도메인 정책 관점으로!테스트의 현상을 중점으로 기술하지 말 것BDD 스타일로 작성하기함수 단위의 테스트에 집중하기보다, 시나리오에 기반한 테스트케이스(TC) 자체에 집중하여 테스트개발자가 아닌 사람이 봐도 이해할 수 있을 정도의 추상화 레벨 권장Given / When / ThenGiven : 시나리오 진행에 필요한 모든 준비 과정When : 시나리오 행동 진행Then : 시나리오 진행에 대한 결과 명시, 검증명확하게 표현하지 못한 테스트는 나중에 우리의 사고를 제한하는 허들이 될 수 있다!추가 인덱스JUnit전통적인 Java 기반의 테스트 프레임워크어노테이션 사용SpockGroovy 기반의 BDD(Behavior-Driven Development) 프레임워크BDD 스타일 테스트로 가독성이 더 좋다.Groovy 언어로 작성해야하므로 작성이 어려울 것 같다.📌 Day 7 미션 & 중간 점검 라이브Day 7 미션 : 변경 포인트는 그 이유를 명확하게 설명할 수 있어야 한다!Day 7 미션은 ‘스스로 스터디 카페 이용권 선택 시스템 리팩토링해보기’였다.미션 수행 전 강의를 들으며 내용들에 대해 거부감 없이 받아들여졌고, 빨리 코드를 짜보고 싶다는 생각이 들도록 하였다. 하지만 실제로 코드를 작성하면서 고민되는 지점들도 꽤 많았고, 근거를 가지고 리팩토링을 하는 과정이 쉽지는 않았다. ‘이게 더 나은거 같은데…?’하며 감으로 진행한 부분도 꽤 있었던 것 같다. 미션을 진행하고 강의를 수강하면서 ‘변경 포인트는 그 이유를 명확하게 설명할 수 있어야 한다.’라는 말이 내가 유의해야 할 말로 느껴졌다.중간 점검 라이브중간 점검 라이브는 Day 4 미션 공통 피드백, Q&A, 코드 리뷰 순서로 진행되었다.Day 4 미션 공통 피드백Day 4 미션 공통 피드백 다음과 같다.추출한 메서드에 static이 있는 경우boolean을 return 하는 메서드에 예외 throw오버 엔지니어링나도 메서드를 추상화하기 위해 추출하다보면, IDE에서 static 키워드를 붙여서 따로 지워준 적이 있다. 추출하여 메서드명만 적지 말고, 전체적으로 확인해보자.상태를 체크하고 boolean을 반환하는 메서드가 있을 때 예외를 던지는 것 보다 상황에 맞게 리팩토링 하는게 좋은 것 같다. validateOrder에서 false를 반환해서 유효하지 않은 주문을 나타내면 되지 않을까?미션의 안내 사항을 잘못 이해한 것 같다. Order의 내부 구현 없이 단순한 리팩토링을 제안하는 것이 요구사항이다. 그런데 나는 ‘Order의 메서드를 추가하더라도 구현 내용은 안적어도 된다.’라고 이해했다. 미션에서 요구했던 내용은 Order에 추가적으로 구현되는 사항 없이 리팩토링을 진행하라고 했던 것 같다.Q&AQ&A의 많은 내용들이 나에게 도움이 되었다. 다른 분들께서 질문해주신 내용들이 생각 외로 많은 인사이트를 얻을 수 있었다.질문하고자 했던 내용들이 다른 분 질문에 있기도 했고 개발 외 질문을 하고 싶어서 우빈님께 개인 취향의 저가 커피 브랜드 1순위와 선호하는 취향에 대해 여쭤보았다.우빈님께서는 저가 커피는 거의 안 드신다고 한다… 그래도 재밌는 영상을 추천해주셨다.항상 카페가면 필터 커피가 있으면 필터 커피를 먹어보거나 그 카페의 스페셜한 원두를 맛을 보려 노력하는데, 항상 먹고 나면 기억이 나지 않는다… ‘이게 어떤 맛이었더라..?’ 하게 된다. 이런 소소한 것에서 취미를 가지는 것도 재밌는 것 같다.코드 리뷰기간 내에 신청해서 우빈님께 코드 리뷰를 받을 수 있었다! 라이브 동안 다른 분 코드 리뷰 해주시는 것도 같이 경청하였는데, 생각보다 내가 놓친 부분들을 여기서 얻을 수도 있었다. 물론 내가 작성한 코드를 리뷰 해주신 것이 직접적으로 바로 와닿았지만, 다른 분 코드 리뷰에서도 배울 점이 있었다.다른 분 코드 리뷰를 해주시면서 퀴즈로 내신 부분이 있다. 퀴즈 내용은 간략하게 설명하면 ‘try-catch에서 예외를 throw했는데 왜 잡히지 않을까?’이다. 코드를 보여주셨을 때 문제가 없어보였다. 그래서 나는 ‘import가 잘못된거 아니야?’라고 생각을 해서 정답을 맞췄다!사실 미션 진행하면서 정말 유사한 문제를 겪은 적이 있었다. 분명 메서드 파라미터가 똑같은데 파라미터가 다르다고 컴파일 에러가 났었다. 미션 특성 상, 맨 처음 리팩토링 이전 패키지와 이후 패키지의 클래스는 똑같다. 그래서 리팩토링 패키지에서 import를 잘못하면, 다른 패키지의 클래스를 가져온다. 그래서 문제가 발생했었는데 이와 똑같은 문제라 운이 좋게도 맞출 수 있었다.영광의 흔적!!코드 리뷰 해주시는 것을 보며 내가 배운 점들은 다음과 같다.Enum의 valueOf 메서드를 통한 생성valueOf 대신 String을 받아서 객체를 생성하는 정적 메서드를 구성사물함 이용 가능 여부를 StudyCafePassType에 저장하는 부분도 좋음get/set은 관용적인 어구로 필드에 대한 getter/setter가 아니더라도, 메서드에 get~~, set~~ 으로 네이밍하는 건 지양하자.early return 보다 else-if가 더 배타적인 내용을 나타내지 않을까?if 절 내에 return을 봐야한다.구조화보다 적절한 책임의 분배가 더 중요하다.위 내용은 다른 분들의 코드 리뷰에서 내가 배운 부분들이다. 다음은 내가 질문하거나 리뷰해주신 내용이다.있을 수도, 없을 수도 있는 필드에 대한 관리를 어떻게 해야할까요?사물함 이용권을 별도 필드로 관리하고 EMPTY 객체를 넣는다? (내용이 정확히 기억이 나지 않는다….)PassReader - FilePassReader로 PassReader의 책임을 부여하고, File에 저장된 Pass를 읽는 구현체로 분리해도 괜찮을까요?괜찮은 접근인 것 같다.Order order = Order.create() 후, order.add(pass) 와 같은 형식으로 주문이후에 주문의 내용이 바뀔 위험성이 존재하므로, 만들어놓고 추가하는 것보다 마지막에 한 번에 Order 객체를 만드는 것이 더 안정적이다. 위험성을 제거하자. 가변보다 불변이 더 좋다!우빈님꼐서 약 1시간 45분동안 라이브를 진행해주셨다. 긴 시간동안 라이브로 진행한다는 것이 쉽지 않으실텐데, 덕분에 나는 유익한 시간을 보낼 수 있었던 것 같다! 직접적으로 우빈님과 소통할 수도 있고 내가 얻을 수 있는 인사이트도 챙겨 소중한 시간이었다. 처음에 말씀하셨던 ‘함께 자라기’를 위해 더 노력해야겠다.
백엔드
・
워밍업스터디
・
발자국
・
클린코드
・
테스트
2025. 03. 09.
1
[인프런 워밍업 스터디] 추상과 객체 지향의 구체화
인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.📌 1주차 기간 강의섹션 2. 추상코드를 잘 짠다는 것은?개발자라면 코드를 잘 짜기 위해 노력한다. ‘코드를 잘 짠다’라는 것은 무엇일까?코드를 잘 짠다는 것은 이해하기 쉬운 코드, 즉 ‘읽기 좋은 코드’라는 것에 동의하지 않는 사람은 없을 것이다.내가 짠 코드를 읽는 대상은 결국 나와 동료이다. 미래의 나와 동료를 위해 매 순간 읽기 좋은 코드를 작성하려고 노력해보자.클린 코드와 리팩토링의 가장 좋은 예시는 테스트 코드 생성 사이클로 볼 수 있다.리팩토링 대상/범위 확인기능 보장을 위한 테스트 코드리팩토링 & 테스트 코드로 검증클린 코드를 추구하는 이유그런데 클린 코드를 추구하는 이유는 뭘까?코드가 잘 읽힌다 ⇒ 이해가 잘 된다 ⇒ 유지보수하기 수월하다 ⇒ 시간과 자원이 절약된다!즉, 클린 코드는 우리의 시간과 자원을 절약해준다.클린 코드, 그리고 추상과 구체추상은 클린 코드를 관통하는 주제이다. 그렇다면 추상이란 뭘까?추상 : 중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버린다.추상의 반대편은 구체라고 볼 수 있다. 하나의 예시를 들어보자.질문A랑 주말에 뭐 했어?답변답변 1 : 식당에 예약해서 갔다왔어.답변 2 : 케치테이블을 통해 가고자 하는 식당에 들어가서 가고자하는 날짜와 시간을 선택하고 그때 식당에 들어가서 밥을 먹고왔어.답변 1과 답변 2는 같은 의미를 나타낸다. 그런데 표현은 다르다. 답변 2는 구체적인 사실이고 답변 1은 추상화된 문장이라고 할 수 있다.구체에서 추상으로 갈수록 정보는 함축되어 제거되고, 추상에서 구체로 갈수록 생략된 정보를 유추하고 재현한다.이를 개발자답게 컴퓨터 과학에 적용해보자.int는 4 byte이고, char는 1 byte이다. 같은 byte로 이루어지지만, 데이터를 어떻게 읽는지에 따라 달라진다. 즉, 데이터 개념에도 추상화가 존재하는 것이다!잠깐 프로그램의 정의에 대해 생각해보자. 프로그램은 다음과 같이 말할 수 있다.프로그램 = 데이터 + 코드위의 예시를 통해 데이터 개념에 추상화가 존재함을 알 수 있었다. 당연히 코드에도 추상화가 존재한다. 또한 데이터 + 코드에도 추상화가 존재한다.흔히 고수준/저수준 언어 라는 용어를 들어봤을 것이다. 고수준과 저수준이 나뉘는 이유는 추상화에 대한 수준을 나타내기 때문이다. 잊고 있을 수 있지만, 컴퓨터는 0과 1밖에 모른다!적절한 추상화는 복잡한 데이터와 복잡한 로직을 단순화하여 이해하기 쉽게 한다!만약 어느 도시에서 예약을 선택이라고 한다고 가정해보자. 그렇다면 답변 1은 다음과 같이 바뀔 것이다.식당을 선택해서 갔다왔어.이 말을 들으면 무슨 말인가 싶다. 말을 통해 유추하거나 재현하기 쉽지 않다. 단순히 말이 안된다고 생각할 수 있지만 이유에 대해 따져보자.추상화 과정에서 중요한 정보를 남기지 않았다.식당을 방문 날짜를 미리 정하는 예약이 아니라, 단순히 선택했다는 정보만 남김상대적으로 덜 중요한 정보를 남기고 중요한 정보를 삭제해석자가 동일하게 공유하는 Context가 없다.중요한 정보의 기준이나 도메인 영역 별 추상화 기준은 다를 수 있음해당 도시에 살았으면 이해 가능잘못된 추상화가 야기하는 사이드 이펙트는 상당히 크다!적절한 추상화란 해당 도메인의 문맥 안에서 정말 중요한 핵심 개념만 남겨서 표현하는 것이다.이름 짓기이름을 짓는다는 행위는, 추상적 사고를 기반으로 한다.단수와 복수 구분하기이름 줄이지 않기은어/방언 사용하지 않기좋은 코드를 보고 습득하기좋은 이름을 짓기 위해 노력하자!메서드 추상화메서드 이름으로 구체적인 내용을 추상화할 수 있어야 한다.서점에서 책을 샀다.서점에 가서 책을 고른다. 책을 계산대에 가져가 직원에게 건네주고 책 가격 금액을 전달하고 책을 얻었다.잘 쓰여진 코드라면, 한 메서드의 주제는 반드시 하나다!그런데 내용이 다음과 같다고 해보자.서점에서 책을 샀다.돈을 인출하고 가는길에 아이스크림을 사먹고, 책을 구매했다.추상화된 내용을 보고 구체적인 내용의 유추가 어렵다. 의미를 담을 수 있는 더 작은 단위로 쪼개야 한다.현금 인출아이스크림 사먹기서점에서 책 구입하기메서드 작성메서드 선언부반환타입메서드명파라미터메서드명추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름파라미터와 연결지어 더 풍부한 의미를 전달할 수도 있다.파라미터파라미터의 타입, 개수, 순서를 통해 의미를 전달파라미터는 외부 세계와 소통하는 창어떤 재료가 필요한지 알려주는 역할이다. 외부 세계한테 필요한 재료를 요구한다.반환타입메서드 시그니처에 납득이 가는, 적절한 타입의 반환값 돌려주기반환 타입이 boolean인데, 이게 이 메서드에서 무엇을 의미하는거지?void 대신 충분히 반환할 만한 값이 있는지 고민해보기반환값이 있다면 테스트도 용이해진다.코드의 줄 수가 많아서 추상화하는 것이 아니다! 같은 라인 수를 가지더라도 추상화할 수 있다.추상화 레벨메서드를 추출한다 ⇒ 외부 세계와 내부 세계를 나누고, 추상화 레벨이 나뉜다.하나의 세계 안에서는, 추상화 레벨이 동등해야 한다!게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임 상태가 1이면게임 종료 메시지 출력1, 2, 3, 4를 쭉 읽다보면 4에서 멈칫하게 된다. 게임 상태 = 1이라는 것의 의미에 대해서 의문이 생기게 된다. 즉, 읽는 사람이 해석을 하게 만든다. 따라서 동등한 추상화 레벨을 맞춰야 한다.게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임을 이겼다면,게임 종료 메시지 출력전보다 훨씬 읽기 수월하다.메서드로 추출한다는 것은 로직이 복잡하거나 의미를 부여할 수 있어서도 맞지만, 추상화 레벨을 동등하게 맞춤으로써 읽는 사람으로 하여금 자연스럽게 이해할 수 있게 한다.매직 넘버, 매직 스트링의미를 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등상수 추출로 이름을 짓고 의미를 부여함으로써 가독성, 유지보수성 증가섹션 3. 논리, 사고의 흐름인지적 경제성코드를 작성할 때 인지적 경제성을 추구하도록 작성해보자!💡 인지적 경제성? - 최소의 인지적 노력으로 최대의 정보 제공 - 한 번에 한 가지 일 즉, 최소한의 인지만 가져가서 최대의 효율을 내보자라는 뜻!코드를 읽는 사람의 메모리를 효과적으로 쓸 수 있도록 해서 읽기 좋은 코드를 작성하자.Early Returnelse if ⇒ 이전의 if에 대해서 생각한다.else ⇒ 앞의 조건들을 모두 알아야 안다.즉, 앞 선 정보들을 모두 기억하고 있어야 함!Early Return을 이용하자!else가 사라짐앞의 코드를 신경 쓸 필요가 없다.사고의 depth 줄이기중첩 분기문, 중첩 반복문2중 for문 내에 if 문이 있다면?3 depth ⇒ 사고의 depth도 많을 것!메서드 추출을 통해 중첩 분기문/반복문을 없애자! 그렇다고 무조건 1 depth로 만들라는 것은 아니다.추상화를 통한 사고 과정의 depth를 줄이는 것이 중요2중 중첩 구조로 표현하는 것이 사고하는 데 더 도움이 된다면 그대로 놔두자.사용할 변수는 가깝게 선언하기사용할 변수는 가깝게 선언하기사용할 변수가 너무 멀리있다면 되돌아가도록 유도한다.공백 라인도 의미를 가진다!복잡한 로직의 의미 단위를 나누어 보여줌으로써 읽는 사람에게 추가적인 정보를 전달할 수 있다.우리나라 글도 문단이 있지 않은가!부정어를 대하는 자세!가 있으면 조건을 먼저 이해하고, 이에 반대되는 조건을 생각하게 된다.가독성이 떨어지고 비틀어서 생각하게 함읽으면서 바로 생각할 수 있게 조건을 작성하자.해피 케이스와 예외 처리예외가 발생할 가능성 낮추기검증이 필요한 부분 = 외부 세계와의 접점의도한 예외와 예상하지 못한 예외 구분사용자에게 보여줄 예외개발자가 보고 처리해야 할 예외항상 NPE가 일어나지 않는지 고려하자.메서드 설계 시 return null 자제Optional은 비싼 객체이고 반환 타입에 사용한다.orElse, orElseGet, orElseThrowe.printStackTrace는 실무에서는 안티패턴이다!섹션 4. 객체 지향 패러다임추상의 관점으로 바라보는 객체 지향절차 지향정해진 순서 차례대로 처리하는 프로그래밍객체 지향객체를 만들어서 객체들 간의 협력을 통해 이루어지는 프로그래밍함수형순수 함수를 정의Side Effect가 없는 함수A ⇒ 항상 A에 대한 정해진 결과가 나오는 것객체란?추상화된 데이터 + 코드객체간의 협력과 객체가 담당하는 책임객체 지향의 특징 ⇒ 캡슐화, 추상화, 상속, 다형성정말 이를 이해하고 적용하고 있는지 스스로 질문해보자!코드 레벨에서 잘 녹여서 쓸 수 있어야 한다.관심사의 분리유지보수가 원활해짐높은 응집도와 낮은 결합도같은 관심사는 응집도가 높아야 한다.각 관심사끼리는 결합도가 낮아야 한다.객체 설계객체로 추상화는..비공개 필드(데이터), 비공개 로직(코드)공개 메서드 선언부를 통해 외부 세계와 소통객체의 책임이 나뉨에 따라 객체 간 협력 발생!객체를 통해절차 지향에서 보이지 않았던 개념 가시화관심사가 한 군데로 모이기 때문에 유지보수성 증가객체를 사용하는 입장에서 보다 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다!!주의사항1개의 관심사로 책임이 분리되었는지 스스로 질문해보자.객체 생성 시, 유효성 검증이 가능setter는 지양, 필요할 경우 메서드명에 의도를 드러내자.getter는 필요한 경우에만.필드 수는 적을 수록 좋다.불필요한 데이터를 굳이 가지고 있지 말자. ex) 주문의 총 가격도메인 지식은 만드는 것이 아니라 발견하는 것!SRP (단일 책임 원칙)하나의 클래스가 하나의 책임만 갖도록 설계해라!하나의 클래스는 한 가지의 변경 이유만 가진다.객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경 되는지 질문하자.변경이유 = 책임관심사의 분리높은 응집도, 낮은 결합도응집도 = 클래스나 모듈 내 요소들이 서로 긴밀하게 연관되어 있는 정도결합도 = 두 개 이상의 객체가 협력할 때, 한 객체가 변경되었을 때 다른 객체가 영향받는 정도각 객체가 독립적인 책임을 가지도록 잘 쪼개진다.객체를 하나의 변경 지점, 하나의 책임으로만 가지도록 설계하자!책임을 발견하기 어려움경계선이 사람마다, 도메인마다 다를 수 있음설계한 객체가 하나의 책임만 가지고 있는지 끊임없이 질문책임을 보는 눈을 기르자!객체를 설계했을 때 책임이 잘 응집되었는가?, 단일 책임만을 가지고 있는가?OCP (개방-폐쇄 원칙)확장에는 열려 있고, 수정에는 닫혀 있어야 한다.기존 코드 변경 없이 기능 확장이 가능해야 함!추상화와 다형성을 활용하여 OCP를 지키자.새로운 요구사항이 추가되었을 때, 기존 코드가 과도하게 변화된다?OCP를 지키지 못하고 있는게 아닌지 생각하자.LSP (리스코프 치환 원칙)상속 구조에서, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.자식 클래스는 부모 클래스의 책임을 준수하고 행동을 변경하지 않아야 한다.부모가 일하는 곳에 자식이 가더라도 문제가 없어야 한다!LSP 위반상속 클래스를 사용할 때 오동작 발생ISP (인터페이스 분리 원칙)클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.기능 단위로 인터페이스를 잘게 쪼개라!내가 구현하려는 인터페이스의 기능 명세에 사용하지 않는 기능이 있다.자신이 사용하지 않는 인터페이스에 의존하게 됨불필요한 의존성으로 인해 결합도가 높아진다.특정 기능의 변경이 여러 클래스에 영향을 미친다.DIP (의존성 역전 원칙)상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안 된다.둘 모두 추상화에 의존해야 한다.의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조하는 것의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화에 의존하는 것저수준 모듈이 변경되어도, 고수준 모듈에는 영향이 가지 않는다.기능을 추상화하여 고수준 모듈은 추상화된 스펙만 참조추상화를 중간에 두고 의존하도록 하자!저수준 모듈이 자유롭게 변경되도 고수준 모듈에는 영향 X💡스프링에서의 DI, IoC DI = Dependency Injection - 필요한 의존성을 직접 생성하는 것이 아니라, 외부에서 주입받는다. - 제 3자(스프링 컨텍스트)가 런타임 시점에 의존성을 주입해준다. IoC = Inversion of Control - 프로그램의 흐름을 프레임워크가 담당 - 빈 생성, 의존성 주입, 생명주기 관리를 스프링 컨테이너가 해준다. 섹션 5. 객체 지향 적용하기상속과 조합상속보다 조합을 사용하자.상속은 시멘트처럼 굳어지는 구조수정이 어렵다.부모와 자식의 결합도가 높다.자식이 부모에 대해서 다 알고 있어야 함부모 수정 ⇒ 자식이 다 영향을 받는다.자식은 부모에 있는 필드를 직접적으로 알고 있음조합과 인터페이스를 활용하는 것이 유연한 구조상속을 통한 코드의 중복 제거가 주는 이점보다, 중복이 생기더라도 유연한 구조 설계가 주는 이점이 더 크다.Value Object도메인의 어떤 개념을 추상화하여 표현한 값 객체값으로 취급하기 위해 불변성 동등성 유효성 검증 등을 보장해야 한다.불변성 : final 필드, setter 금지동등성 : 다른 인스턴스여도 내부 값이 같으면 같은 값 객체로 취급유효성 검증 : 객체가 생성되는 시점에 값에 대한 유효성 보장VO vs EntityEntity식별자 O식별자가 아닌 필드의 값이 달라도, 식별자가 같으면 동등한 객체로 취급!equals & hashcode도 식별자 필드를 통해 재정의VO식별자 X내부의 모든 값이 다 같아야 동등한 객체일급 컬렉션일급 시민다른 요소에게 사용 가능한 모든 연산을 지원하는 요소변수로 할당될 수 있다.파라미터로 전달될 수 있다.함수의 결과로 반환될 수 있다.일급 함수함수형 프로그래밍에서 함수는 일급 시민함수는 변수에 할당될 수 있고, 인자로 전달될 수 있고, 함수의 결과로 함수가 반환될 수도 있다.일급 컬렉션컬렉션을 포장하면서, 컬렉션만을 유일하게 필드로 가지는 객체컬렉션을 다른 객체와 동등한 레벨로 다루기 위함단 하나의 컬렉션 필드만을 가진다.컬렉션을 추상화하며 의미를 담고, 가공 로직의 보금자리가 생긴다.가공 로직에 대한 테스트도 작성 가능getter로 컬렉션을 반환할 일이 생긴다면, 외부 조작을 피하기 위해 새로운 컬렉션을 만들어 반환해야 한다.Enum의 특성과 활용enum은 상수의 집합상수와 관련된 로직을 담을 수 있음특정 도메인 개념에 대해 종류와 기능 명시변경이 잦은 개념은 DB로 하는게 나을 수도 있다.enum은 코드 ⇒ 배포를 해야만 변경할 수 있다.변경이 잦은 요소라면 DB로 관리하자.숨겨져 있는 도메인 개념 도출하기도메인 지식은 만드는 것이 아니라 발견하는 것객체 지향은 현실을 100% 반영하는 도구가 아니라, 흉내내는 것현실 세계에서 쉽게 인지하지 못하는 개념도 도출해서 사용해야 할 때가 있다.설계할 때는 근시적, 거시적 관점에서 최대한 미래를 예측하고, 시간이 지나 틀렸다는 것을 인지하면 언제든 ㄷ돌아올 수 있도록 코드를 만들어야 한다.완벽한 설계 X, 그 당시의 최선이 있을 뿐 📌 1주차 수강 후 느낀점강의 제목들을 보면, 사실 개발자라면 다 한번씩 들어봤을 내용이다. SOLID 원칙, 추상화, VO, Enum 등등 누군가 질문하면 그럴듯한 답변을 할 수 있는 주제다. 그런데 ‘이 내용들을 깊이 있게 이해하고 있을까?’ 라는 질문에는 아니었던 것 같다.이러한 개념들을 직접 코드 레벨에 적용하며 개선하는 과정에서 배울 점이 많았다. 내가 알고 있던 깊이는 상당히 얄팍했다. 가끔 헷갈리면 검색해서 ‘아 대충 이런내용이었지.’하고 금방 닫곤 했는데, 이번 기회에 직접 경험해보며 내 지식들을 구체화시킬 수 있었다. 개발자가 가볍게 여기고 넘어갈 수 있는 주제들에 대해 다시 생각하며, 기본기의 중요성을 알 수 있었다! 그리고, 기존에 프로젝트하며 고민했던 지점이 있었다. 추상화를 할 때 ‘같은 라인 수를 가져도 굳이 해아하나?’ 생각했었는데, 나한테 해답이 되는 1주차였던 것 같다!! 전부터 ‘한 번 들어야지!’했던 강의인데 워밍업 스터디를 계기로 수강하길 잘한 것 같다🙂 📌 1주차 미션Day 2 : 추상과 구체 예시 생각해보기뭔가 추상화를 실생활에 생각해본 적은 없었는데, 느낌이 새로웠다. 되게 개발스러운 단어라고만 생각했는데 추상은 우리의 삶에도 가까이 적용되는 단어라는 것을 느꼈다.농구에서 슛을 한다고 해보자. 이때 무릎을 구부리고, 팔꿈치를 올리고, 무릎을 다시 피고, 팔꿈치를 다시 피고, 손목을 피며 공을 던진다. 위 여러 과정들이 ‘슛’으로 추상화된다. 우리 삶의 추상화는 상당히 자주 쓰인다!Day 4 : [섹션 3. 논리, 사고의 흐름] 내용을 중심으로 리팩토링하기이전에는 코드를 리팩토링하면, 되게 느낌적으로 다가갔던 것 같다. ‘뭔가 이게 나은 것 같긴한데..’하며 접근하거나 단순히 코드 라인을 줄이는 등 이유와 근거가 빈약했다. 하지만 Day 4 미션을 통해 리팩토링의 이유와 근거를 통해 리팩토링을 진행해보았다.필요한 정보를 빼내 추상화부정어구 최소화케이스 확인위 과정을 통해 미션에서 주어진 코드를 읽기 좋게 개선하였다. 읽으면서 사고의 흐름이 멈칫하거나 꼬일 수 있던 코드를 바로 읽어나갈 수 있게 작성하려고 노력했다.강의를 들으면서 내가 납득됐던 내용에 대해 직접 리팩토링에 적용하여 ‘읽기 좋은 코드’를 만들어 나갈 수 있었다. 단순히 불필요한 작업을 없애는 것만이 리팩토링이 아니라, ‘읽기 좋은 코드’를 만드는 것도 중요한 리팩토링이라는 것을 깨달았다!
백엔드
・
추상
・
객체지향
2025. 03. 05.
0
[인프런 워밍업 스터디] Readable Code - 추상과 구체
인프런 ‘Readable Code: 읽기 좋은 코드를 작성하는 사고법’을 수강한 후, 작성한 내용입니다.코드를 잘 짠다는 것은?개발자라면 코드를 잘 짜기 위해 노력한다. ‘코드를 잘 짠다’라는 것은 무엇일까?코드를 잘 짠다는 것은 이해하기 쉬운 코드, 즉 ‘읽기 좋은 코드’라는 것에 동의하지 않는 사람은 없을 것이다.내가 짠 코드를 읽는 대상은 결국 나와 동료이다. 미래의 나와 동료를 위해 매 순간 읽기 좋은 코드를 작성하려고 노력해보자.클린 코드와 리팩토링의 가장 좋은 예시는 테스트 코드 생성 사이클로 볼 수 있다.리팩토링 대상/범위 확인기능 보장을 위한 테스트 코드리팩토링 & 테스트 코드로 검증클린 코드를 추구하는 이유그런데 클린 코드를 추구하는 이유는 뭘까?코드가 잘 읽힌다 ⇒ 이해가 잘 된다 ⇒ 유지보수하기 수월하다 ⇒ 시간과 자원이 절약된다!즉, 클린 코드는 우리의 시간과 자원을 절약해준다.클린 코드, 그리고 추상과 구체추상은 클린 코드를 관통하는 주제이다. 그렇다면 추상이란 뭘까?추상 : 중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버린다.추상의 반대편은 구체라고 볼 수 있다. 하나의 예시를 들어보자.질문A랑 주말에 뭐 했어?답변답변 1 : 식당에 예약해서 갔다왔어.답변 2 : 케치테이블을 통해 가고자 하는 식당에 들어가서 가고자하는 날짜와 시간을 선택하고 그때 식당에 들어가서 밥을 먹고왔어.답변 1과 답변 2는 같은 의미를 나타낸다. 그런데 표현은 다르다. 답변 2는 구체적인 사실이고 답변 1은 추상화된 문장이라고 할 수 있다.구체에서 추상으로 갈수록 정보는 함축되어 제거되고, 추상에서 구체로 갈수록 생략된 정보를 유추하고 재현한다.이를 개발자답게 컴퓨터 과학에 적용해보자.int는 4 byte이고, char는 1 byte이다. 같은 byte로 이루어지지만, 데이터를 어떻게 읽는지에 따라 달라진다. 즉, 데이터 개념에도 추상화가 존재하는 것이다!잠깐 프로그램의 정의에 대해 생각해보자. 프로그램은 다음과 같이 말할 수 있다.프로그램 = 데이터 + 코드위의 예시를 통해 데이터 개념에 추상화가 존재함을 알 수 있었다. 당연히 코드에도 추상화가 존재한다. 또한 데이터 + 코드에도 추상화가 존재한다.흔히 고수준/저수준 언어 라는 용어를 들어봤을 것이다. 고수준과 저수준이 나뉘는 이유는 추상화에 대한 수준을 나타내기 때문이다. 잊고 있을 수 있지만, 컴퓨터는 0과 1밖에 모른다!적절한 추상화는 복잡한 데이터와 복잡한 로직을 단순화하여 이해하기 쉽게 한다!만약 어느 도시에서 예약을 선택이라고 한다고 가정해보자. 그렇다면 답변 1은 다음과 같이 바뀔 것이다.식당을 선택해서 갔다왔어.이 말을 들으면 무슨 말인가 싶다. 말을 통해 유추하거나 재현하기 쉽지 않다. 단순히 말이 안된다고 생각할 수 있지만 이유에 대해 따져보자.추상화 과정에서 중요한 정보를 남기지 않았다.식당을 방문 날짜를 미리 정하는 예약이 아니라, 단순히 선택했다는 정보만 남김상대적으로 덜 중요한 정보를 남기고 중요한 정보를 삭제해석자가 동일하게 공유하는 Context가 없다.중요한 정보의 기준이나 도메인 영역 별 추상화 기준은 다를 수 있음해당 도시에 살았으면 이해 가능잘못된 추상화가 야기하는 사이드 이펙트는 상당히 크다!적절한 추상화란 해당 도메인의 문맥 안에서 정말 중요한 핵심 개념만 남겨서 표현하는 것이다.이름 짓기이름을 짓는다는 행위는, 추상적 사고를 기반으로 한다.단수와 복수 구분하기이름 줄이지 않기은어/방언 사용하지 않기좋은 코드를 보고 습득하기좋은 이름을 짓기 위해 노력하자!메서드 추상화메서드 이름으로 구체적인 내용을 추상화할 수 있어야 한다.서점에서 책을 샀다.서점에 가서 책을 고른다. 책을 계산대에 가져가 직원에게 건네주고 책 가격 금액을 전달하고 책을 얻었다.잘 쓰여진 코드라면, 한 메서드의 주제는 반드시 하나다!그런데 내용이 다음과 같다고 해보자.서점에서 책을 샀다.돈을 인출하고 가는길에 아이스크림을 사먹고, 책을 구매했다.추상화된 내용을 보고 구체적인 내용의 유추가 어렵다. 의미를 담을 수 있는 더 작은 단위로 쪼개야 한다.현금 인출아이스크림 사먹기서점에서 책 구입하기메서드 작성메서드 선언부반환타입메서드명파라미터메서드명추상화된 구체를 유추할 수 있는, 적절한 의미가 담긴 이름파라미터와 연결지어 더 풍부한 의미를 전달할 수도 있다.파라미터파라미터의 타입, 개수, 순서를 통해 의미를 전달파라미터는 외부 세계와 소통하는 창어떤 재료가 필요한지 알려주는 역할이다. 외부 세계한테 필요한 재료를 요구한다.반환타입메서드 시그니처에 납득이 가는, 적절한 타입의 반환값 돌려주기반환 타입이 boolean인데, 이게 이 메서드에서 무엇을 의미하는거지?void 대신 충분히 반환할 만한 값이 있는지 고민해보기반환값이 있다면 테스트도 용이해진다.코드의 줄 수가 많아서 추상화하는 것이 아니다! 같은 라인 수를 가지더라도 추상화할 수 있다.추상화 레벨메서드를 추출한다 ⇒ 외부 세계와 내부 세계를 나누고, 추상화 레벨이 나뉜다.하나의 세계 안에서는, 추상화 레벨이 동등해야 한다!게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임 상태가 1이면게임 종료 메시지 출력1, 2, 3, 4를 쭉 읽다보면 4에서 멈칫하게 된다. 게임 상태 = 1이라는 것의 의미에 대해서 의문이 생기게 된다. 즉, 읽는 사람이 해석을 하게 만든다. 따라서 동등한 추상화 레벨을 맞춰야 한다.게임 시작 멘트 출력게임 초기화게임 보드 보여주기게임을 이겼다면,게임 종료 메시지 출력전보다 훨씬 읽기 수월하다.메서드로 추출한다는 것은 로직이 복잡하거나 의미를 부여할 수 있어서도 맞지만, 추상화 레벨을 동등하게 맞춤으로써 읽는 사람으로 하여금 자연스럽게 이해할 수 있게 한다.매직 넘버, 매직 스트링의미를 갖고 있으나, 상수로 추출되지 않은 숫자, 문자열 등상수 추출로 이름을 짓고 의미를 부여함으로써 가독성, 유지보수성 증가
백엔드
・
추상
・
구체
2025. 03. 03.
0
10%의 가치를 향해서 나아가기 (지식공유자 박우빈님)
인프런 지식공유자 박우빈님의 강의내용을 기반으로 작성된 글 입니다!최근 몇 년 사이에 AI 라는 단어가 더 가까이 다가온 듯 합니다. 과거 알파고를 통해 AI를 간접적으로 경험하였지만, 제 생활과는 거리가 먼 이야기 같았습니다. 하지만 이제는 모두가 ChatGPT를 알고, IT 업계가 아니더라도 잘 활용되고 있습니다. 또한 최근에는 딥시크가 화제에 오르기도 하였습니다.AI!AI…AI는 세상을 급변화시키고 있고, 현재 엄청난 생산성을 가져다주는 도구입니다. 하지만 반대로 이로 인해 대체되는 일도 생기게 됩니다.개발자! 개발자의 미래는 어떻게 될까요?AI는 PMF(제품 시장 적합성, Product Market Fit) 임계값을 급변시켜 붕괴를 일으킬 수 있다!PMF 임계값을 급변시킨다는게 뭐지..?우빈님께서 Chegg라는 서비스를 예시로 들어주셨습니다. Chegg는 대학생의 과제를 도와주는 서비스입니다. 그런데 지금 대학생들은 대부분 ChatGPT를 사용합니다. 이 결과는 주가를 통해서도 알 수 있습니다..AI 등장 이후, 급격한 하락세를 겪게 되었습니다.즉, PMF 임계값이 급변했다는 것은 잘 진행되던 서비스의 붕괴를 시사할 수 있습니다.근데 개발자는 괜찮을까요..?이와 관련하여 불안감이 느껴지게 하는 말들도 존재합니다.“GPT-4를 보았을 때, GUI 이후로 가장 중요한 기술의 발전을 보았다는 걸 깨달았다.” - 빌게이츠“ChatGPT 사용 후, 제 기술 중 90%의 가치가 0으로 떨어졌습니다.” - 켄트 백2, 3년 전에는…시니어 한명이 3-4명의 주니어들에게 도움을 주는 방식이었습니다. 해결하기 어려운 문제가 있으면, 주로 시니어에게 도움을 받으며 해결하였습니다. 즉, 시니어와 주니어의 강결합도가 높았습니다.그런데 지금은?AI가 한 사람마다 붙어있습니다. 시니어도, 주니어도 GPT를 활용해서 일을 할 수 있습니다.비교적 단단했던 비전문가/준전문가와 전문가의 경계가, 지금은 경계가 모호해졌습니다.그럼 이 경계는 앞으로 아예 사라지는걸까요?전문가의 역량과거의 전문가 지식은 이렇게 말할 수 있습니다.60% : 단순 지식20-30% : 수준 있는 전문 지식10% : 경험 지식, 통찰, 상위 설계그런데 지금은, 이렇게 대체할 수 있을 것 같습니다.60% : ChatGPT30% : 약간의 지식 + ChatGPT10% : 경험 지식, 통찰, 상위 설계위의 언급된 말 중 이 문장이 있습니다.“ChatGPT 사용 후, 제 기술 중 90%의 가치가 0으로 떨어졌습니다.” - 켄트 백그런데 켄트 백은 이 뒤에 한 마디를 더했습니다.“그런데 나머지 10%의 레버리지는 1,000배 상승했어요.” - 켄트 백이 말은 우리가 나아가야 할 방향을 시사하고 있습니다. 위의 90% 일은 AI가 해결할 수도 있습니다. 그러나 나머지 10%는 AI가 도와주는 산출물을 언제, 어떻게, 어디에 쓸 것인가에 대한 판단, 결정, 경험, 그리고 시야입니다. 즉, 10% 내용을 가지고 전문가의 정의가 재보정됩니다. 여기서 클린 코드와 테스트 코드 학습은 10%의 시야를 기르는 과정이라고 할 수 있습니다.따라서, 이번 워밍업 클럽 스터디를 단순한 지식을 위한 참여가 아니라 시야를 기르는 과정이라고 바라보려고 합니다!클린 코드는 무엇이, 어떻게 변화할 것인지에 대한 안목과 대비를,테스트 코드는 무엇을, 왜 검증해야 하는가에 대한 판단과 결정을.그럼 어떻게 학습하면 좋을까?‘함께 자라기’가 가져오는 Exponential한 성장.인프런 워밍업 스터디에서 제가 추구해야할 것은 ‘함께 자라기’를 통해 자극 주고 받기입니다! 여기서 말하는 자극은 Comfort Zone에서 벗어나게끔 해주는 자극을 말합니다.'함께 자라기'가 주는 긍정적인 효과는 저도 확실하다고 생각합니다. 동료를 통해 얻는 자극은 좋은 동기부여가 됩니다. '이 분은 같은 내용을 이렇게 바라보셨구나.', '이 분은 성장하기 위해 정말 많은 노력하시는구나'와 같은 자극으로 저도 나아갈 힘을 받게 됩니다. 혼자 공부하는 것이 몸도 마음도 편하지만, 함께하면서 얻을 수 있는 내용은 상당히 값진 내용입니다.사실 요즘 개발에 대한 관심도가 떨어졌던 것 같습니다. 취업을 준비하며 이력서나 자기소개서도 다듬는 시간도 생기다보니 잡생각이 많기도 하였고, 그냥 저 자체로도 해이해졌음을 느꼈습니다. 이번 인프런 워밍업 스터디를 계기로 강의를 수강하며 새로운 인사이트도 얻고, 교류를 통해 얻는 자극과 지식으로 채울 수 있는 한 달의 시간을 보낼 수 있도록 노력해야겠습니다!
개발 · 프로그래밍 기타