소개
호주에 살고 있는 소프트웨어 개발자입니다. 30년간 다양한 분야의 시스템과 서비스를 개발해본 경험이 있습니다.
스프링 프레임워크와 관련 기술을 좋아하고 JVM 기반 언어를 주로 사용합니다.
한국스프링사용자모임(KSUG)을 설립하고 활동했고, 토비의 스프링이라는 책을 쓰기도 했습니다.
개발과 관련된 다양한 주제에 관해 이야기하는 것을 좋아합니다.
강의
수강평
- 토비의 스프링 6 - 이해와 원리
- 토비의 스프링 부트 - 이해와 원리
- 토비의 스프링 6 - 이해와 원리
게시글
질문&답변
실무에서 Clock 사용 여부
시간대(Timezone)이 하나면 충분한 경우라면 LocalDateTime을 쓰면 됩니다. 절대 시간을 저장해두고 여러 시간대로 전환하는 게 필요한 경우엔 OffsetDateTime이나 ZoneDateTime을 사용합니다.이때 시간대를 옮겨가며 복잡한 시간 계산이 필요한 경우라면 Clock이 유용합니다. 그 외에는 사실 Clock을 잘 사용하지 않습니다. 다만, 테스트를 엄밀하게 하는 목적을 위해서, 혹은 장기적으로 글로벌 서비스가 되어서 여러 시간대를 오고가면서 시간 계산을 해야한다고 하면 Clock 빈을 기반으로 시간을 가져오는 방식을 표준으로 잡는 것이 좋습니다.
- 0
- 2
- 19
질문&답변
오브젝트의 정의에 대해 궁금한점이 있습니다.
객체(오브젝트)는 클래스의 인스턴스이다라는 건 자바 언어 스펙에 나오는 정의입니다.오브젝트와 인스턴스는 동일하다고 생각하셔도 되고요.말씀하신 컴파일 시점의 타입은 오브젝트 타입이 아니라 오브젝트 "레퍼런스 변수"의 타입니다. 컴파일 시점엔 어떤 오브젝트(인스턴스)가 사용될지는 모르죠. 다만, 이후 코드의 타입을 확인하기 위해서 그 오브젝트를 가리키는 레퍼런스 변수가 어떤 타입인지 코드에 정의한 것입니다.List data = getData();이런 코드가 있다면 컴파일러는 data 변수는 List 인터페이스 타입에 할당할 수 있는 호환되는 어떤 오브젝트가 올 것이라고 기대하고 코드를 체크하는 것이죠. 실제로 getData()에서 만들어서 리턴하는 오브젝트는 ArrayList 타입의 오브젝트일 수도 있고, LinkedList 타입의 오브젝트일 수도 있겠죠.컴파일 시점에는 "변수"의 타입이 고정되는 것이고, 런타임에는 그 변수가 가리키는(레퍼런스 하는) 오브젝트의 타입이 확정되는 것이라고 이해하시면 됩니다.더 설명이 필요하다면 말씀해주세요.
- 0
- 2
- 190
질문&답변
강의 재생 안됨
영상 재생이 안 되시는 건지요. 이건 인프런의 문의하기 기능을 이용해서 인프런 운영팀에게 요청을 해주시는 게 좋겠습니다.
- 0
- 2
- 40
질문&답변
Test 결과 화면이 다른 거 같은데 설정 문제일까요?
테스트를 실행하는 IntelliJ의 설정에 따라서 방식이 다르기 때문인 것으로 생각됩니다.task가 많이 뜨는 건 제가 Gradle을 이용해서 테스트를 실행하는 기본 설정으로 되어있기 때문일 겁니다. 사실 어떤 방식으로 테스트를 수행하는 거의 차이는 없습니다. 그래도 같은 화면을 보고 싶으시면 설정에서 저와 같은 방식으로, Run test using 이부분을 Gradle로 해주시면 될 듯합니다.(사진)
- 0
- 2
- 56
질문&답변
NoSQL의 PlatformTransactionManager adapter 는 없는건가요?
안녕하세요. NoSQL 중에서 스프링이 지원하는 기술들은 대부분 트랜잭션 관리 기능이 구현되어있습니다. PlatformTransactionManager 추상화를 이용하고, @Transactional도 적용이 가능하죠. 사용 기술을 스프링이 지원하는지 확인해보시고, Spring MongoDB 등과 같이 제공되는 경우에는 레퍼런스 문서에서 Transaction으로 검색해보시면 사용방법을 확인하실 수 있습니다.
- 0
- 2
- 62
질문&답변
스프링 부트와의 강의 순서 질문
학습 순서는 스프링 6를 먼저 들으시고, 스프링 부트로 개발하는 간단한 경험을 해보신 뒤에 스프링 부트 강의를 들으시면 좋습니다. 스프링 3.x라고 하셨는데 10년 이전에 개발된 레거시 시스템을 유지보수하시는 게 아니라면 아마도 스프링 부트 3.x를 쓰실 것 같네요. 스프링 부트를 써보지 않으셨다면 인프런의 무료 강의 또는 온라인에 공개된 간단한 스프링 부트 예제를 한번 따라 해보시고, 제 부트 강의를 들으시면 좋을 겁니다.
- 0
- 2
- 132
질문&답변
ServiceLocatorFactoryBean에 궁금한점이 있습니다.
로그인 전략을 구현한 빈이 매번 새로 만들어질 게 아니라면 미리 정의해두고, 필요한 타입과 주입 정보를 이용해서 가져다 생성자, 세터 등으로 주입 받아 사용하는게 맞습니다. 의존성 주입이 가능한데 의존성 룩업을 사용할 이유는 없습니다. 매번 새로운 빈을 만드는 프로토타입이 아닌 경우라면요. 당연히 그것도 Provider 방식을 이용하면 되는데, 지금 말씀하신 로그인 전략은 그럴 필요가 없어보입니다. 다만, 이 경우 LoginStrategy를 구현한 빈이 여러개라면 주입 받을 때컬렉션으로 모두 가져와서 필요한 걸 선택해서 사용하거나사용할 로그인 방식에 따라서 bean name이나 qualifier 등으로 특정 strategy 빈을 가져와 사용하는 방식이 필요할 겁니다.강의의 어느 부분을 보시다가 이 내용이 알고 싶으셨는지 궁금하군요. 제가 답변을 드릴 수 있는 건 얼마든지 답을 해드리겠습니다만, 가능하면 강의 내용과 관련된 질문을 해주시면 감사하겠습니다.
- 0
- 1
- 58
질문&답변
인용구의 출처가 궁금합니다.
조영호 님이 예전에 쓰신 블로그 글 시리즈에서 인용한 내용입니다. 그 뒤로 블로그 서비스가 종료되어서 지금은 그 글을 온라인에서는 찾을 수 없는 걸로 알고 있습니다.
- 0
- 1
- 81
질문&답변
애플리케이션 예외 사용에 대한 질문
날카로운 질문을 해주셨네요. 지금 예로 드신 InsufficientBalanceException을 복구가능한 정상적인 작업흐름, 그러니까 비즈니스적으로 의미있는 설계된 플로우의 하나로 볼 것인가, 또 그게 어느 정도의 비율로 나타날 것인가 등에 따라서 사실 접근 방법은 조금씩 다른 듯합니다. InsufficientBalanceException을 정상적인 비즈니스 로직의 케이스의 하나로 보지만 극히 드물게 일어난다고 가정을 해보죠. 예를 들어 프론트에서 이미 잔고를 확인한 상태에서만 이체 요청을 한다거나 결제를 하도록 만들었는데, 하지만 그 순간 다른 결제가 돌아가면서 잔액이 부족해지는 경우가 있습니다. 아주 드물지만 없지 않죠. 비율이 얼마나 될까요. 전체 이체 요청 만 건당 하나라고 보면, 이건 예외적인 상황이 맞습니다. 그래서 이런 건 시스템 장애나 버그에 의한 예외는 아니지만 극히 예외적인 상황이라서 Exception을 사용하고 싶은 거죠. 그러면서 이런 시도를 했다는 기록을 DB에 남기고 싶다면, 체크예외를 쓸 수 있습니다. 그러면 이체 자체를 제외한 나머지 DB 작업은 트랜잭션이 커밋되면서 남게 되고, 이 예외를 앞의 Controller가 받아서 이 경우에 사용자에게 보내줄 알림을 전해줄 수 있겠죠. 그런데 이런 0.01% 수준의 예외적인 케이스를 위해서 리턴 값에 의미를 부여해서 잔액 부족은 -1. 이런식으로 정해서 이걸 if로 잡는 코드를 넣으면, 이를 처리하는 코드가 뭔가 메인이 되는 느낌이죠. 게다가 응답 값에 의미를 부여하는 것은 자칫 버그가 발생하기 쉽죠. 어떤 개발자는 이 서비스 코드를 호출하면서 0보다 작으면 에러인가 이럴 수도 있고, 어떤 개발자는 이거 상수로 정의해야 하는 거 아닌가 해서 INSUFFICIENT_BALANCE = -1 이렇게 정의해서 쓸 수도 있고, 나중에 예외 값은 -100으로 하자고 고치게 되면 로직이 틀어지겠죠. enum으로 정의할 수도 있지만, 다른 응답 값이 있는 경우에 예외 경우를 체크하기 위한 enum을 같이 리턴하려면 또 클래스로 만들어야 하죠. 그래서 이때는 체크 예외를 만들어서 이런 경우가 있을 수도 있다는 걸 사용하는 컨트롤러 등의 코드에 명확하게 알려주고, 이때 대응하는 코드를 만들도록 강제할 수 있습니다. 아마 책에서는 이걸 강조해서 설명했던 것 같습니다. 강의에서 이 케이스를 다루지는 않았지만, 핵심은 복구 불가능한 예외는 예외로 처리하고 굳이 핸들링하지 않는게 맞다는 것을 이야기했습니다. 이 잔액 부족은 복구 가능한 것일까요? 여기서 예외를 프론트엔드까지 다루는 시나리오에 따라서 여러 판단이 있을 수 있을 듯합니다. 이걸 의미있게 처리하기 위해 정상 플로우의 특별한 상황으로 정의하고 API에서 특정 응답을 주도로 만들고, 프론트는 이런 잔액부족 상황을 알게 되면 다시 최신 잔액을 확인하는 API를 호출하게 만들고, 안내를 주도록 할 수 있죠. 또는 이걸 응답에서 그래서 부족한 이유와 잔액을 이 케이스에서는 API 응답으로 만들 수도 있습니다.또는 이건 어쨌든 처리할 수 없는 예외상황이니 메시지만 딱 보여주고 말겠다면, 서비스에서부터 런타임 에러를 던지고, @ControllerAdvice에서 그 Exception에 담긴 에러 메시지를 담아서 리턴하고, 프론트는 에러 메시지를 띄워주고, 사용자는 다시 잔액 확인하러 뒤로 가고.. 등등의, 정말 예외적인 케이스니까 굳이 이걸 처리하는 코드를 복잡하게 만들지 않도록 할 수 있습니다.어쨌든 예외 상황을 리턴 값으로 다루지는 마시고, 복구 가능한 예외인지 아니면 일반적인 정말 예외 상황인지에 따라서 예외 타입을 결정해서 사용하시면 될 듯합니다.
- 0
- 1
- 63
질문&답변
프로퍼티 빈의 후처리기 도입 AnnotationUtils의 사용
오, 이건 제가 몰랐던 방법이네요. 이렇게 annotation.prefix()를 바로 이용하는 게 더 간단하겠네요.
- 0
- 2
- 91