저작권 안내: 저작권자표시 Yes 상업적이용 No 컨텐츠변경 No

스프링5 입문

JSP 2.3

JPA 입문

DDD Start

인프런 객체 지향 입문 강의


"OKKYCON 2017 소통, 개발에 숨을 불어넣다"에서 집중해서 들은 세션 내용 요약:


개인적 소감은 "가길 잘했다"!


XP 더 나은 소프트웨어 품질을 위한 일의 방법 (정윤진)


프로젝트를 통해 배운 것

  • 작은 규모 팀이 효율적
  • 다른 전문성이 모여 어려운 문제 해결
  • 문제를 즉각 공유해야 빠른 해결 가능
  • 팀 구성원의 성향이 다르면 감정적 문제가 커지는 경향
  • 스타트업은 제품보다는 보통 팀이 깨져서 무너짐
작은 팀은 소통에 도움이 됨
  • 소통 횟수가 줄어듬
  • 팀이 커지면 소통 비용 증가(허락을 구하기 위한 미팅 시간 증가)
  • 팀이 작으면 쉽게 공유
밸런스팀
  • 매니저, 사용자, 엔지니어, 데이터 사이언스 등으로 구성된 팀
  • 가치: 신뢰, 경험 공유(배움), (작은) 실패를 환영하는 문화, 다양한 목소리
  • 적은 스트레로 더 대단한 걸 더 빠르게 이룸
XP/TDD
  • 결국 모든 것은 빠르고 안전하게 제품을 내보내기 위함
  • 예: TDD - 여러 이유로 테스트 작성을 포기하지만, 지속적으로 빠르게 가려면 테스트 필수
  • 예: 페어 - 돈이 아까울 수 있으나, 2명이 같이 일하면 생산성은 20% 떨어지나 품질은 80% 향상
피보탈 업무 패턴
  • 8:30 아침 식사 (대화)
  • 9:00 스탠드업 (5분 진행)
  • 9:05 팀 스탠드업 (페어 결정), 해결할 스토리는 트래커에 존재, 늦어도 9:30분을 넘기지 않음
  • 9:20 페어 업무 시작
  • 11:30 점심 식사
  • 13:00 오후 시작
  • 18:00 아무도 없음

채용시 하루 동안 페어를 하고 결정한다는 것이 인상적



애자일은 애자일이란 단어를 버려야 한다. (신정호)


애자일의 형식적인 부분을 버려야 함. 형식으로 망하는 프로젝트 징후:

  • 애자일에 대한 맹신
  • 목표 없는 작은 주기
  • 의미 없는 회의
  • 기준 없는 추정
  • 자발적이지 않은 교육
  • 겉치레 시스템 평가
역할

직책보단 역할에 집중

  • 스크럼 마스터니 PO니 하는 직책보다는 "파악, 결정, 책임" 역할을 수행해야 함
파악
  • 파악을 하지 못하면 결과적으로 결정할 수 없음
  • 파악하기 위한 도구: 정책, 요구사항 기록, 통신 프로토콜, UI/UX, 진행사항, 테스트 케이스, 커밋기록, 현황판 중심 데일리 미팅, 리스트 목록 등
  • 팀내/팀외적으로 놓치는 일이 없는지 계속 확인 필요
결정
  • 결정에 필요한 배경, 결정할 케이스를 객관식으로 제공
책임
  • 파악 -> 좋은 결정 -> 책임은 작아짐(없어짐)
구성원(내부/외부 포함) 간 협업, 부딪칠 수 있는 경우를 정리
  • 기획자 vs 디자이너
    • 기획자는 와이엎프레임 없이 요구사항 위주 정리, 디자이너는 사용자에 맞는 디자인
  • 기획자 vs 개발자 vs 품질 책임자
    • 테스트케이스 중심 개발(상호 간 인식 차이 줄임), 테스트케이스 공유 시간
  • 기획자 vs 품질 책임자
    • 정책이 정해지지 않은 경우 데일리 정책 회의
  • 디자이너 vs 개발자
    • TODO 목록
  • 최고의사결정자 vs 팀/TFT
    • 일정 관련 정량적인 수치 기반 상황 공유
  • 팀 vs 팀

스케줄링


중요한 건 결국 정량적 파악. 다음을 고려:

  • 근무일수, 휴가, 테스트 일수
  • 개발 완료 기준
  • 늘 어디쯤인지 파악할 수 있어야 함

스트린트 회의

  • 정책과 요구사항 발표
  • 구현 이슈 논의
  • 모호한 상황을 명확하게 파악하고 결정
  • 회고를 했다면 이어서 진행
스트린트
  • 기획 중심 기간, 구현 중심 기간, 테스트 기간 중심에 따라 알맞게 진행
    • 예, 기획 중심 기간에는 부문 미완성이지만 흐름을 먼저 보임
    • 예, 구현 중심 기간에는 부문 완성으로 기능을 먼저 보임

회고


회고는 해결한 것과 해결할 것을 명확히 하는 시간

  • 진행 상황 공유
    • 목표로 했는데 한 것 못 한 것 공유
    • 결과 보기(가장 중요)
    • 이슈 현황판, 데일리 미팅 기록
    • 남은 일(테스트케이스로 관리)
  • 리스크 확인
  • 마지막 스트린트에 좋은 점, 나쁜 점(감정 고려)


애자일은 말이나 형식이 아닌 행동으로 보여주는 것



협업도구로 제대로 말하기 (김동수)


협업 도구를 제대로 쓰게 하는 것이 어려웠음

  • 구성원들의 변화 필요성이 있거나 확실히 지금 문제를 해결할 솔루션이 되는 경우, 정착할 가능성이 높아짐

협업 도구를 변경하고 싶다면

  • 협업을 더디게 만드는 것을 식별
  • 구성원의 요구사항 수집
  • 영향력있는 조력자(대표, staff 부서) 필요
  • 비용 발생시, 최종 결정권자를 설득

협업 도구 평가 기준

  • 사용이 쉬운가
  • 검색할 수 있는가
  • 공유가 쉬운가
  • 다른 도구와 연동
  • 사용료 대비 가치가 충분한가

마켓컬리 적용

  • 도구에 대한 교육 진행
  • 도구에 대한 계정 관리를 관리팀에 맡김
  • 프로필 사진은 소통에 도움
도구로서의 말
  • 상대방이 이해할 수 있는 단어
  • 단어가 정확해야 함
  • 날짜가 정확
  • 검색 가능하게
  • 에티켓


생산성 지향의 커뮤니케이션과 기업 문화(최영근)


커뮤니케이션

  • 소프트 스킬 > 하드 스킬 (소프트 스킬이 18% 더 높음)
  • 소통 == passive skill
    • 소통에서 90% 전달은(10% 손실) 매우 낮은 완성도임, 6회 쌓이면 53%
  • 소통은 정보 전달 목적 -> 청자 중심

소통법

  • 경영진에게
    • 신속한 의사 결정 목적 -> 단기적 효과, 숫자 사용
    • 집행 요약(executive summary) 사용
  • 세일즈
    • '고객이 원하잖아요'에 숨어있게 하지 말라
    • 요구를 끌어내는 대화 (목적, 대안 등을 끌어낼 필요)
  • 자기 자신
    • 철저한 자기 검열 필요 (나를 위한 결정은 아닌지)
    • 성과를 위한 결정인가?

관성, 조직

  • 규칙화, 문서화(성문화), 선언 --> 무언의 압박
  • 넥플릭스 사례: 스포츠팀처럼
    • 계약 관계, 전문가, 조직의 비즈니스 목표 달성
  • 유비쿼터스 언어
  • 회의록 작성
    • goal, discussion point, agreement, action item을 담음
  • 박터지게 싸우자
    • 팁1, 우리 잘되려고 하는 거지 --> 나 자신도 흥분을 가라 앉히는 효과
    • 팁2, 설득되면 확실하게 리액션
  • 메신저 : 정원사 필요
    • 정원사는 검열을 하지 않는다는 것을 느끼게 해야 함

협업의 미신 5가지 - 근거 기반 협업으로 가기 위해 (김창준)


미신1 : 프로그래밍은 잘 하는데 협업은 잘 못 해? 또는 반대 협업은 잘 하는데 프로그래밍은 딸려?


프로그램과 협업은 별개라는 사고!


뿌리:

  • 잘못된 연구 방식 (1960년대)
    • 뛰어난 프로그래밍을 측정한 기준이 잘못 됨(예, 문제를 잘 푼 사람이 뛰어난 프로그래머라 가정하는 식)
    • 한 명으로 실험실에서 혼자두고 측정

1990년대부터 여러 명을 두고 측정해 보니,

  • 뛰어난 프로그래머 그룹의 조언 : 사회적 조언 포함 (70%)
  • 평균적인 그룹의 조언 : 사회적 조언이 적음 (20%)
  • 실제로 성과를 잘 내는 프로그래머일수록 대화에 많은 시간 투입
  • 프로그래밍을 재정의해야 하는 순간이 옴
협력도 프로그래밍의 한 부분으로 인지하기 시작


협업은 프로그래밍을 잘 하는데 있어 필수적인 요소



미신2 : 팀의 퍼포먼스는 가장 뛰어난 사람이 결정


MIT의 연구:

  • CQ(집단 지성) - 어떤 팀은 어떤 일을 줘도 잘 하고, 어떤 팀은 어떨 일을 줘도 못함
    • 가장 잘 하는 사람, 평균 IQ도 팀의 성과에 영향을 주지 못함
  • CQ 성과에 영향을 주는 지표 
    • 공감력
    • 말을 골고루 함(여러 명이 골고루 할 때)
구글 연구: 탁월한 팀의 특정 찾기
  • 팀의 성과는 개기인의 기술적 전문성이 중요하지 않음,
  • 심리적 안정감이 높을수록 성과가 좋음

일이 복잡할수록 더 그러함


미신3 : 전문가를 모아두면 협력을 잘 할 것이다


  • 전문성에서 사회적인 요소를 빼고 봄
  • 오래 본다고 협력이 늘지 않음 (예, 양치질), 협력 훈련을 받은 적이 거의 없음
  • 협력할지 논의한 뒤에 일을 진행하면 성과가 좋아질 수 있음

의식적인 협력의 중요성 : 어떻게 협력할까를 먼저 고민



미신4 : 협력을 잘 하는 것은 서로 좋은 관계를 유지하는 것이다


  • 미신: 협력 잘 하는 것 = 인간 관계 좋은 것 = 술 자주 마시는 것
  • 회피적으로 좋은 관계인지 고민 필요 -> 갈등을 처리하는데 능숙하지 못함
  • (건설적으로) 부정적인 감정을 얘기할 수 있는 팀이 성과가 좋음, 솔직하게 불만을 얘기할 수 있어야 좋은 관계

미신5 : 분업을 잘 하는 것이 협업을 잘 하는 것이다

  • 분업 외에는 협업에 대한 아이디어가 없음
  • 리차드 행만: 
    • 팀과 워크 그룹 구분
      • 팀 : 구성원 사이에 네트워크로 연결
      • 워크 그룹 : 구성원 사이에 트리/스타 구조로 연결
    • 팀의 성과가 압도적으로 높음
    • 일이 불확실할수록 팀의 성과가 높음
  • 왜 잘하게 되느냐?
    • 기본적으로 피어 코칭 때문(동료 간 소통)
  • 공통점, 팀의 효과성을 예측하는 변수 : 상호 간 모니터랑하는 팀 -> 성과가 높음
    • 서로 의견 제시하는 팀

70 20 10

    • 성과의 70%는 팀원이 만나기 전에 이미 결정
    • 20%는 시작하는 시점에 결정
    • 10%는 일을 하는 과정에서 결정
  • 미리 사전에 어떻게 일할 지를 미리 정하고, 초반에 굉장히 신경 써야 함
분업을 너무 많이 해서 문제가 된 에피소드: "131번 서버 누구 담당입니까?"


상호 작용이 중요함


QA에 나온 이야기

  • 조직의 미션: 개인의 일을 한다는 느낌을 없애야 함!
    • 우리 팀의 최고 우선순위가 뭡니까?
  • 사람이 계속 바뀌는 환경에서는 더더욱 사회적인 요소에 신경을 많이 써야 함
  • 의견 내기가 무섭다 --> 일단 우리 안에서 해보는 것!
    • 예: 단계를 구분 - 아이디어를 내는 것과 누가/어떻게 할 지를 정하는 것을 구분
      • 누가 할지 고를 때 팀이 함: 독박이 아니고 팀원이 협력하게 유도
      • 누군가 해야 하는데 하기 싫은 일 -> 팀이 회의를 해서, 어떻게 전환을 하면 재미있어질 것인지 논의
  • 문화 -> 작은 상호작용이 쌓이면서 주변에 영향을 미침


Posted by 최범균 madvirus

댓글을 달아 주세요

Posted by 최범균 madvirus

댓글을 달아 주세요

HTTP 요청 헤더를 이용해서 접속한 사용자 정보를 받기로 했다. 받아야 할 정보가 두 개여서 스프링 컨트롤러에서 다음과 같은 코드를 사용하게 되었다.


@RestController

public class HandoverApi {


    private HandoverService handoverService;


    @PostMapping("/api/handover")

    public HandoverPk postHandover(

            @RequestHeader(name = "employeeId", required = false) String employeeIdHeader,

            @RequestParam(name = "employeeId", required = false) String employeeIdParam,

            @RequestHeader(name = "cellphone", required = false) String cellphoneHeader,

            @RequestParam(name = "cellphone", required = false) String cellphoneParam,

            @Valid HandoverRequest req) throws ServletRequestBindingException {

        if (StringUtils.isEmpty(employeeIdHeader) && StringUtils.isEmpty(employeeIdParam)) {

            throw new ServletRequestBindingException("Missing employeeId");

        }

        if (StringUtils.isEmpty(cellphoneHeader) && StringUtils.isEmpty(cellphoneParam)) {

            throw new ServletRequestBindingException("Missing cellphoneParam");

        }

        String employeeId = employeeIdParam != null ? employeeIdParam : employeeIdHeader;

        String cellphone = cellphoneParam != null ? cellphoneParam : cellphoneHeader;

        req.setEmployeeId(employeeId);

        req.setCellphone(cellphone);


        return handoverService.handoverDigWork(req);

    }


employeeId와 cellphone 값을 요청 헤더나 요청 파라미터로 받을 수 있도록 했다. 헤더와 파라미터를 함께 사용할 수 있게 해서 코드가 다소 장황해졌다. 문제는 이런 코드가 계속해서 중복해서 출현하게 되었다는 것이다. employeeId와 cellphone 값이 필요한 API에서 이런 장황하면서 보기 싫은 코드를 중복해서 사용하게 되었다.


이런 중복을 없애기 위해 스프링의 커스텀 HandlerMethodArgumentResolver를 사용했다. 원하는 코드 모양은 다음과 같다.


@RestController

public class HandoverApi {


    @PostMapping("/api/handover")

    public HandoverPk postHandover(

            @MobileUser(check = MobileUserCheck.ALL) MobileUserInfo mobUserInfo,

            @Valid DigWorkHandoverRequest req) {

        req.setEmployeeId(mobUserInfo.getEmployeeId());

        req.setCellphone(mobUserInfo.getCellphone());


        return handoverService.handoverDigWork(req);

    }


@MobileUser 애노테이션이 붙은 MobileUserInfo 타입 파라미터에 자동으로 employeeId와 cellphone 값이 담기도록 스프링을 확장하는 것을 목표로 했다.


파라미터에 사용할 MobileUserInfo와 @MobileUser


먼저 employeeId와 cellphone을 담을 데이터 클래스를 하나 만들었다.


@Getter

@AllArgsConstructor

@ToString

public class MobileUserInfo {

    private String employeeId;

    private String cellphone;

    

    public void checkEmployeeId() throws ServletRequestBindingException {

        if (StringUtils.isEmpty(employeeId))

            throw new ServletRequestBindingException("Missing employeeId");

    }

    

    public void checkCellphone() throws ServletRequestBindingException {

        if (StringUtils.isEmpty(cellphone))

            throw new ServletRequestBindingException("Missing cellphone");

    }


    public void checkAll() throws ServletRequestBindingException {

        checkEmployeeId();

        checkCellphone();

    }

    

}


@MobileUser 애노테이션은 다음과 같이 정의했다.


@Target(value = { ElementType.PARAMETER })

@Retention(value = RetentionPolicy.RUNTIME)

public @interface MobileUser {


    MobileUserCheck check() default MobileUserCheck.EMPLOYEE_ID;


}


check 속성은 값을 어디까지 검사할지 여부를 지정하기 위한 용도로 사용한다. 이 예제의 경우 employeeId만 필요한 경우가 있고, cellphone만 필요한 경우가 있고, 둘 다 필요한 경우도 있다. 이를 필요에 따라 검사 대상을 지정할 수 있도록 check 속성을 추가했다. MobileUserCheck 열거 타입은 검사할 대상을 포함한다.


public enum MobileUserCheck {

    ALL, EMPLOYEE_ID, CELLPHONE, NONE

}


커스텀 HandlerMethodArgumentResolver 구현


스프링이 컨트롤러 메서드의 인자로 MobileUserInfo 타입 객체를 받을 수 있으려면 HandlerMethodArgumentResolver의 구현체를 알맞게 제공해야 한다. 예제를 위한 구현체는 다음과 같다.


public class MobileUserInfoResolver implements HandlerMethodArgumentResolver {

    @Override

    public boolean supportsParameter(MethodParameter parameter) {

        MobileUser mobUserAnnot = parameter.getParameterAnnotation(MobileUser.class);

        return mobUserAnnot != null && 

                 MobileUserInfo.class.isAssignableFrom(parameter.getParameterType());

    }


    @Override

    public Object resolveArgument(MethodParameter parameter, 

            ModelAndViewContainer mavContainer,

            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) 

            throws Exception {

        String employeeIdHeader = webRequest.getHeader("employeeId");

        String employeeIdParam = webRequest.getParameter("employeeId");

        String cellphoneHeader = webRequest.getHeader("cellphone");

        String cellphoneParam = webRequest.getParameter("cellphone");


        String employeeId = employeeIdParam != null ? employeeIdParam : employeeIdHeader;

        String cellphone = cellphoneParam != null ? cellphoneParam : cellphoneHeader;


        MobileUser mobUserAnnot = parameter.getParameterAnnotation(MobileUser.class);

        MobileUserInfo mobileUserInfo = new MobileUserInfo(employeeId, cellphone);

        switch (mobUserAnnot.check()) {

        case ALL:

            mobileUserInfo.checkAll();

            break;

        case EMPLOYEE_ID:

            mobileUserInfo.checkEmployeeId();

            break;

        case CELLPHONE:

            mobileUserInfo.checkCellphone();

            break;

        default:

            break;

        }

        return mobileUserInfo;

    }

}


supportsParameter() 메서드는 컨트롤러 메서드의 특정 파라미터를 지원하는지 여부를 리턴한다. 이 예제의 경우 파라미터에 MobileUser 애노테이션이 붙이 있고 파라미터 타입이 MobileUserInfo인 경우 true를 리턴하도록 구현했다.


resolveArgument() 메서드는 파라미터에 전달할 객체를 생성한다. 이 예에서는 employeeId와 cellphone 값을 요청 헤더나 요청 파라미터에서 읽어와 MobileUserInfo를 생성하고, @MobileUser 애노테이션의 check 값에 따라 값 검사를 수행한 뒤에, 검사에 통과하면 MobileUserInfo를 리턴한다.


WebMvcConfigurer로 설정하기


마지막으로 준비할 작업은 커스텀 HandlerMethodArgumentResolver를 사용하도록 스프링 MVC를 설정하는 것이다. @EnableWebMvc나 스프링 부트를 사용한다면 다음과 같이 WebMvcConfigurer 구현 클래스를 사용해서 커스텀 HandlerMethodArgumentResolver를 등록하면 된다.


@Configuration

public class WebMvcCustomConfiguration extends WebMvcConfigurerAdapter {


    @Override

    public void addArgumentResolvers(

            List<HandlerMethodArgumentResolver> argumentResolvers) {

        argumentResolvers.add(new MobileUserInfoResolver());

    }

    

}



커스텀 인자 사용


이제 컨트롤러 메서드의 파라미터로 커스텀 인자 타입을 사용하면 된다.


@PostMapping("/api/handover")

public HandoverPk postHandover(

        @MobileUser(check = MobileUserCheck.ALL) MobileUserInfo mobUserInfo,

        @Valid DigWorkHandoverRequest req) {

    req.setEmployeeId(mobUserInfo.getEmployeeId());

    req.setCellphone(mobUserInfo.getCellphone());


    return handoverService.handoverDigWork(req);

}


@GetMapping(value = "/api/checks")

public yCheckData getCheckData(@MobileUser MobileUserInfo userInfo) {

    CheckData checks = checkService.getChecks(userInfo.getEmployeeId());

    return checks;

}


Posted by 최범균 madvirus

댓글을 달아 주세요