[리팩토링]팩토리 메서드(Factory Method)패턴을 활용하여 동적으로 구현객체 생성하기

기존코드에서 원래 의도하지 않게 프로그래밍하여 나쁜 냄새를 풍기고 있던 코드를

과감하게 리팩토링했다.

우선, 스프링MVC에서 기존 서비스단 코드는 아래와 같다.

@Service
public class TestService {
    @Resource(name = "notLoginedWeekDisplay")
    private WeekDisplay notLoginedWeekDisplay;

    @Resource(name = "loginedWeekDisplay")
    private WeekDisplay loginedWeekDisplay;

    public String display(Member member, int currentWeek) {
        if(member.isGuest()) {
            return notLoginedWeekDisplay.display(currentWeek);
        }

        return loginedWeekDisplay.display(currentWeek);
    }
}

display메서드의 의도는 아래와 같다.

  • 로그인사용자와 비로그인 사용자별로 각기 다른 데이터를 가져와 뷰에 보여줌
  • 데이터는 현재주차와 나머지 다른 주차를 비교하여 만들어짐

로그인/비로그인 사용자별로 뷰를 보여주는 부분은 따로 추상화하여

WeekDisplay 추상클래스를 만들었다.

public abstract class WeekDisplay {
    public abstract String compareSmallerOrEqualWeek(int compareWeek);

    //현재주간과 비교주간을 비교해 결과를 만드는 공통로직
    public String display(int currentWeek) {
       //비교주간(ex: 1~4주)이 현재주간과 같거나 이전이면
       //로그인,비로그인 조건별로 다르게 구현
       if (compareWeek <= currentWeek) {
           return compareSmallerOrEqualWeek(compareWeek);
       }

       //비교주간이 현재주간과 다른경우
       ....
    }
}

그리고, WeekDisplay에서 로그인/비로그인 사용자별로 추상메서드

compareSmallerOrEqualWeek()를 각기 다르게 구현하였다.

즉, “템플릿메서드“패턴을 사용하여 공통로직은 추상클래스인 AbstractWeekDisplay에

위치하고 로그인/비로그인 사용자별로 다르게 동작해야 되는 부분은 따로 추상메서드인

compareSmallerOrEqualWeek()로 빼서 각 구현체별로 다르게 구현하였다.

@Component
public class NotLoginedWeekDisplay extends WeekDisplay {
    @Override
    public String compareSmallerOrEqualWeek(int compareWeek) {
        String week = Integer.toString(compareWeek)+"주차";
        reutrn week +" 비로그인 사용자가 보는 화면입니다.";
    }
}

@Component
public class LoginedWeekDisplay extends WeekDisplay {
    @Override
    public String compareSmallerOrEqualWeek(int compareWeek) {
        String week = Integer.toString(compareWeek)+"주차";
        reutrn week +" 로그인 사용자가 보는 화면입니다.";
    }
}

아래 그림과 같이 추상화를 잘해놓고도, 한가지 간과한 점이 있었다!

siege_class_diagram_1

문제는 바로 로그인/비로그인 조건에 따라 서비스단에서 분기를 태워

WeekDisplay의 구현체의 display()메서드를 바로 호출한다는 점이다.

원래 의도대로라면 아래 코드가 되었을 것이다.

    public String display(Member member) {
        //분기로직 없이 동적으로 AbstractWeekDisplay의 구현체를 가져와
        //데이터를 얻어온다.
        //weekDisplay 구현체는 member여부에 따라 달라진다.
        return weekDisplay.display();
    }

그래서, 해당 분기로직을 삭제하고 로그인/비로그인 조건에 따라 동적으로

WeekDisplay의 구현객체를 가져오도록 “팩토리 메서드“패턴을 사용하였다.

    @Service
    public class WeekDisplayFactory {
        private static WeekDisplay notLoginedWeekDisplay;

        @Resource(name = "loginedWeekDisplay")
	private WeekDisplay loginedWeekDisplay;

	public WeekDisplay create(Member member) {
	    if (member.isGuest()) {
	        if (notLoginedWeekDisplay == null) {
		    return new NotLoginedWeekDisplay();
		}

		return notLoginedWeekDisplay;
	    }

	    return loginedWeekDisplay;
	}
    }

위와 같이 팩토리클래스를 사용함으로서, 서비스단은 아래와 같이 간단해졌다.

public String display(Member member) {
    //weekDisplayFactory는 서비스단에서 DI받음
    WeekDisplay weekDisplay = weekDisplayFactory.create(member);

    return weekDisplay.display();
}

드디어, 원래 의도였던 동적으로 WeekDisplay의 구현객체를 가져와서

화면에 보여주는 것을 실현했다.

그리고 서비스단의 분기로직도 사라졌다.

아래는 이번 리팩토링작업동안 진행했던 테스트케이스 코드이다.

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
    @Mock
    private WeekDisplayFactory weekDisplayFactory;

    @Captor
    private ArgumentCaptor<Member> memberArgumentCaptor;

    @InjectMocks
    private TestService service;

    private WeekDisplay notLoginedWeekDisplay;
    private WeekDisplay loginededWeekDisplay;

    @Before
    public void setUp() {
        //각종 초기화 코드들..
    }

    @Test
    public void 비로그인사용자의경우() {
        when(weekDisplayFactory.create(guestMember)).thenReturn(notLoginedWeekDisplay);

	String result = service.display(guestMember);

	verify(weekDisplayFactory).create(memberArgumentCaptor.capture());

	assertThat(guestAccount, sameInstance(memberArgumentCaptor.getValue()));
        assertThat(result, is("비로그인 사용자가 보는 화면입니다."));
    }

    @Test
    public void 로그인사용자의경우() {
        when(weekDisplayFactory.create(loginedMember)).thenReturn(loginedWeekDisplay);

	String result = service.display(loginedMember);

	verify(weekDisplayFactory).create(memberArgumentCaptor.capture());

	assertThat(loginedMember, sameInstance(memberArgumentCaptor.getValue()));
        assertThat(result, is("로그인 사용자가 보는 화면입니다."));
    }
}
카테고리: 개발 | 댓글 남기기

[독후감]Ajax DOM 스크립팅

ajax_dom_scripting

올해 처음으로 읽은 기술서적이다.  저자인 김영보님은 페이스북을 통해 알게되었고,

기본“을 중요시하는 책 소개를 보고 망설임없이 책을 선택했다.

클라이언트 사이드는 서버사이드에 비해 많이 부족한 것이 사실이었고, 막상 사용자가

접하게 되는 최종경로가 “유저 인터페이스“임에도 불구하고 그 동안 너무 무관심(?)하게

대하지 않았나 하는 반성과 함께 책을 읽어나갔다.

다 읽고 난 후 일단, 기존에 몰랐던 DOM(Document Object Model)에 대한 기본적인 이해와

함께 “자바(Java)“처럼 인터페이스를 통해 각 기능이 제공하는 프로퍼티와 메서드에

접근할  수 있다는 것을 알았다.

그리고 DOM을 사용하는 궁극적 목적인 “역동적인 유저 인터페이스를 실현“하기 위함

이라는 것도 알게 되었다.

그리고 이 책 덕분에 W3C에서 DOM API Specification도 살펴보게 되는 계기가 되었다.

가장 좋았던 것은, 중간중간 깨알같이 나오는 문구들이다. 저자의 깊은 지식과 경험을

엿볼 수 있는 대목이기도 하다. 예를 들면 아래와 같다.

객체지향 프로그램을 개발함에 있어 가장 기본적인 접근은 더 이상 기능을 나눌 수

없을 때까지 상세하게 분리하는 것이다.

다만, 한가지 아쉬운 것은 DOM API를 기준으로 설명하다보니 책을 읽어 나가는 것이

다소 지루할 수 있다는 것이다.

그리고 책에 나와있는 예제들을 직접 실행해보지 않은 것도 아쉬움으로 남는다.

이 책에 있는 예제들의 prototypejs를 jquery로 변경해서 연습해보는 것도 좋을것이다.

서버사이드에 비해 클라이언트 사이드가 관심이 적은 건 사실이지만, 앞서 얘기한 대로

클라이언트 사이드를 무시하고 소프트웨어를 개발한다는 것은 어불성설이기 때문에

서버사이드와 함께 꾸준히 관심을 가지고 공부해 나갈 생각이다.

다음엔 아마 자바스크립트를 공부하지 않을까 싶다. ^^

카테고리: 자바스크립트 | 태그: , , | 댓글 남기기

[독서]2013 올해 읽은 책

* 양서

* 기술

  • 트러블 슈팅이야기(사내스터디)
  • 웹 표준 Ajax DOM 스크립팅(김영보, 3/4)
카테고리: 독서, 일상과의 대화 | 태그: | 댓글 남기기

[2012]올해를 마치며.. 회고!

올해 연말에 회사가 바빠서 연말인지도 잘 모르겠지만 또 한해를 보내기에 앞서

잠시나마 나를 돌아보는 회고의 시간을 가져보려 한다.

우선 올 한해 나에게 있어 굵직한 일들 위주로 돌아보자

================================================================

1. 회사에서의 웹사이트 두번 오픈

올해 두번의 회사 웹사이트를 오픈하면서  해본 일은

  • 태그형 게시판
  • 웹에서의 실시간 알림 Redis + Nodejs (페이스북 알림과 흡사한..)
  • 회원쪽에서의 SSO(Single Sign On) 진행중
  • 스프링배치 경험
  • 리눅스 명령어 조금 익숙해짐
  • 빌드, 배포, 프로젝트 형상관리에 관심을 갖게 됨 : Groovy 책을 사게 됨!

이 정도인듯 싶다. 적고 보니 별건 없네.

하지만 지금도 여러모로 많이 보고 느끼고 삽질하며 경험하고 있다.

2. GliderWiki OSS 대회 은상 수상

개인적으로 올해 가장 기쁜일이 아닌가 싶다. 작년 11월부터 기존 스터디 인원과

의기투합하여 시작했던 한국형 위키 오픈소스 프로젝트가 결실을 맺어

공개SW대회에서 11월 자유부문 은상을 수상하게 되었다.

2012-11-27 12.07.11

 

프로젝트에 대한 회고는 아래 url을 참고하면 된다.

[oss 공개소프트웨어 대회]오픈소스 프로젝트 회고

내년엔 더욱 업그레이드 된 프로젝트로 만들고 싶다.

3. 독서율 50%이상 초과달성!

한해 40권 완독을 목표로 도전했는데,  전공 9권

그 밖의 양서 12권 해서 21권을 완독율 70%이상 읽어서

목표치 50%를 초과달성하였다.

다만, 아쉬운 것은 전공서적의 완독율이 전년보다는 훨씬

좋아졌지만 그래도 많이 낮다는 것이다.

새해에는 이것저것 기울이기보다 하나를 깊숙이 파고 이해하는

것이 필요하다

4. 좋은 프로그래머분들과의 지속적인 만남

오프라인에서 개인적으로 SNS를 통해 친분을 쌓고 만나오던

분들과 지속적으로 좋은 관계를 유지한 한해였다.

페이스북의 Taeki Kim, 최범균님이 그 분들이다.

나보다 선배님들이고 풍부한 지식과 경험을 듣고 나눌 수 있어

기쁜 한해였다.

내년도 좋은 인연으로 좋은 분들을 많이 만났으면 하는 바램이다.

5. 올해 진행한 스터디 3개

올해했던 스터디는

  • HTML5 + CSS3(사내)
  • 웹을 지탱하는 기술(사내)
  • 토비의 스프링 3.1(사내)

그나마 회사에 다니면서 스터디를 한 것 같다.

올해는 공개소프트웨어 대회 준비로 스터디를 하기가

힘든 한해였다.

6. 현재 관심있는 5가지 테마

내가 현재 관심있는 테마는

  • Project Build 
  • Project Deploy
  • Mashup Service
  • SNS Push
  • Starcraft App

저 5개이다. 내년엔 저 5개에 대한 서비스를 구체적으로 만들어 볼 생각이다

카테고리: 개발 | 태그: | 댓글 남기기

[리팩토링]GliderWiki – 공간저장 리팩토링

이제 출품과 심사가 모두 끝나고 시간날때 하나씩 내가 만든 코드를 리팩토링 하기로 했다.

참고로 글라이더 위키의 사이트와 소스는 아래를 참고하면 된다.

기존에는 서비스단의 저장메서드에 여러가지 기능들이 절차지향적으로 혼재되어 있었다면

이번 리팩토링의 가장 큰 목적은 각 기능별로 메서드를 분리하는 것이었다.

공간쪽 프로세스는 크게 3가지로 나눌 수 있다.

  • 공간데이터 저장
  •  권한 관련 데이터처리
  • 공간이미지관련 파일copy, 데이터처리

우선 리팩토링 전 소스를 보면..

WeSpace addSpaceInfo = new WeSpace(weSpace.getWe_space_name(), weSpace.getWe_space_desc(),
				weSpace.getWe_space_exposed(), weSpace.getWe_view_privacy(), weSpace.getWe_edit_privacy(), weUserIdx,
				weUserId);
		entityService.insertEntity(addSpaceInfo);

		// 생성, 수정권한에 대한 DB처리
		WeSpace afterSpaceInfo = (WeSpace) entityService.getRowEntity(addSpaceInfo);

		if (!weSpace.getWe_view_privacy().equals(AuthorityType.ALLGROUP)) {
			authorityTypesProcess(VIEW_AUTHORITY, weSpace.getWe_view_data(), afterSpaceInfo.getWe_space_idx(),
					afterSpaceInfo.getWe_view_privacy());
		}

		if (!weSpace.getWe_edit_privacy().equals(AuthorityType.ALLGROUP)) {
			authorityTypesProcess(EIDT_AUTHORITY, weSpace.getWe_edit_data(), afterSpaceInfo.getWe_space_idx(),
					afterSpaceInfo.getWe_edit_privacy());
		}

		// 프로필관련 DB처리
		if (!StringUtils.isEmpty(weSpace.getWe_upload_imgName())) {
			ImageInfo imgInfo = processImage(imageInfo, weSpace.getWe_upload_imgName());
			String imgPath = weSpace.getWe_upload_imgName().substring(0,
					weSpace.getWe_upload_imgName().lastIndexOf('/'));
			String imgName = weSpace.getWe_upload_imgName().substring(
					weSpace.getWe_upload_imgName().lastIndexOf('/') + 1, weSpace.getWe_upload_imgName().length());
			WeSpaceImage weSpaceImage = new WeSpaceImage(afterSpaceInfo.getWe_space_idx(), imgPath, imgName,
					imgInfo.getWidthSize(), imgInfo.getHeightSize());
			entityService.insertEntity(weSpaceImage);
		}

각 기능들이 하나의 메소드안에 모두 들어가있고, 절차지향적으로 만들어져 있어서

가독성이 매우 떨어짐을 알 수 있다.

리팩토링 후의 소스를 다시 보면..

public void create(WeSpace weSpace, Integer userIdx, ImageInfo imageInfo) throws Throwable {
		WeSpace addSpaceInfo = new WeSpace(weSpace.getWe_space_name(), weSpace.getWe_space_desc(),
				weSpace.getWe_space_exposed(), weSpace.getWe_view_privacy(), weSpace.getWe_edit_privacy(), userIdx,
				userIdx);
		entityService.insertEntity(addSpaceInfo);

		// 생성, 수정권한에 대한 DB처리
		WeSpace savedSpaceInfo = (WeSpace) entityService.getRowEntity(addSpaceInfo);
		savedSpaceInfo.setWe_view_data(weSpace.getWe_view_data());
		savedSpaceInfo.setWe_edit_data(weSpace.getWe_edit_data());
		savedSpaceInfo.setWe_upload_imgName(weSpace.getWe_upload_imgName());
		processAuthorityTypes(savedSpaceInfo);

		// 프로필관련 DB처리
		processSpaceImage(savedSpaceInfo, imageInfo);
	}

우선 공간데이터 저장이외 나머지 부분을 개별메서드로 분리함을 확인할 수 있으며,

그에 따라 코드 가독성이 훨씬 좋아졌음을 알 수 있다.

이 중에서 권한을 처리하는 processAuthorityTypes()메서드는 디자인패턴 중

전략패턴“을 사용하여 리팩토링을 진행했다.


public interface AuthorityProcessor {
	Map<String, Object> getPrivacyData();
}

public void processAuthorityTypes(final WeSpace weSpace) throws Throwable {

	if (weSpace.isAllGroupViewPrivacy()) {
		AuthorityProcessor processor = new AuthorityProcessor() {

			@Override
			public Map<String, Object> getPrivacyData() {
				Map<String, Object> result = Maps.newHashMap();

				result.put("authorityType", weSpace.getWe_view_privacy());
				result.put("authorityData", weSpace.getWe_view_data());
				result.put("authorityPrivacy", VIEW_AUTHORITY);

				return result;
			}
		};

		divideFromAuthrityData(weSpace.getWe_space_idx(), processor);
	}

	if (weSpace.isAllGroupEditPrivacy()) {
		AuthorityProcessor processor = new AuthorityProcessor() {

			@Override
			public Map<String, Object> getPrivacyData() {
				Map<String, Object> result = Maps.newHashMap();

				result.put("authorityType", weSpace.getWe_edit_privacy());
				result.put("authorityData", weSpace.getWe_edit_data());
				result.put("authorityPrivacy", EDIT_AUTHORITY);

				return result;
			}
		};

		divideFromAuthrityData(weSpace.getWe_space_idx(), processor);
	}

}

리팩토링을 하면서 아래 그림에서 보다 싶이 가장 윗단의 조회/수정 정책에서 정해서

내려오면 될 것을,  그룹이나 구성원으로 내려갔다가 다시 조회/수정 정책으로 분기하는

코드를 떼어낼 수 있었다.

우선 추상화를 통해 권한처리 중에 다르게 동작하는 부분을 인터페이스로 분리하고,

다르고 동작하는 부분을 익명클래스의 메서드안에 구현함으로써 리팩토링을 진행했다.

그리고, 공통으로 처리해야할 부분은 템플릿 메서드로 빼내서 다르게 동작하는 부분의

객체를 템플릿 메서드로 전달해서 공통으로 수행해야할 동작을 처리했다.

구현하면서 오버엔지니어링이라는 생각이 들었고, 실제로도 리팩토링을 다 하고 난 뒤의

결과를 보니, 이 정도로 리팩토링을 할 사항은 아닌것 같다는 생각이 들었다.

하지만 의미없게 넘기던 메서드의 파라미터는 최소한 제거할 수 있었음에 소기의 목적을

이룰 수 있었다.

한 가지 아쉬운 점은 리팩토링을 하면서 추상화를 통해, 이게 진정 맞는 추상화인가라는

의문이 계속 들었다는 점이다. 내가 해당 디자인패턴에 끼워맞추기 위해 추상화를

한 것이 아닐까라는 의구심이 생긴것이다.

그렇다면 앞서 말한 오버엔지니어링을 깊게 의심해 볼 수 있을 것이다.

리팩토링을 하는 것도 중요하지만, 오버엔지니어링이 되는 시점을 아는 것도

참 중요한 일이 아닐까 생각한다.

그리고 아래는 이번에 리팩토링한 저장쪽의 테스트코드이다.

@Mock
private EntityService entityService;

@Mock
private EntityDao entityDao;

@Mock
private FileService fileService;

@Mock
SpaceDao spaceDao;

@Spy
private SpaceService spaceService = new SpaceService();

@Captor
private ArgumentCaptor<WeSpace> weSpaceCaptor;

private WeSpace weSpace;

@Before
public void setup() {
	weSpace = new WeSpace("테스트공간", "공간설명", "Y", AuthorityType.ALLGROUP, AuthorityType.USER, 1, 1);
	injector(spaceService).set(entityService).set(spaceDao).set(fileService);
}

@Test
public void create() throws Throwable {
	// 공간정보를 DB에 저장한다
	ImageInfo imageInfo = new ImageInfo("/temp", "/real");
	WeSpace savedSpace = new WeSpace();
	savedSpace.setWe_space_idx(1);
	weSpace.setWe_view_data("");
	weSpace.setWe_edit_data("1,2");
	weSpace.setWe_upload_imgName("/upload/test.jpg");

	when(entityService.getRowEntity(weSpaceCaptor.capture())).thenReturn(savedSpace);

	spaceService.create(weSpace, 1, imageInfo);

	verify(entityService).insertEntity(weSpaceCaptor.getValue());

	// 조회와 수정정책에 따라 관련된 데이터를 DB에 저장한다
	verify(spaceService).processAuthorityTypes(savedSpace);

	// 공간이미지파일을 지정된 위치에 복사하고, 이미지 정보를 DB에 저장한다
	verify(spaceService).processSpaceImage(savedSpace, imageInfo);

	WeSpace ws = weSpaceCaptor.getValue();

	assertThat(ws.getWe_space_name(), is("테스트공간"));
	assertThat(ws.getWe_space_desc(), is("공간설명"));
	assertThat(savedSpace.getWe_edit_data(), is("1,2"));
	assertThat(savedSpace.getWe_upload_imgName(), is("/upload/test.jpg"));
}

기존에 있던 테스트코드를 모두 지우고, 다시 만들면서 느낀 점은

  • 테스트코드가 없는 코드를 리팩토링하는건 정말 힘들다.(테스트하기가 너무~ 힘듦)
  • 테스트코드는 개발자가 꼭 해야할 항목!

이다. 특히나 ArgumentCaptor에 대한 개념을 잡는것도 큰 도움이 되었다.

원본코드에서 새로 생성한 객체가 메서드의 인자로 넘어갈 때, 테스트코드와의

객체생성시점이 달라서 메서드의 인자에 대한 테스트를 어떻게 할까 고민했는데,

Mockito에 저 놈이 딱 있지 않은가?

===================================================

이제 하나씩 리팩토링하면서, 그 코드를 테스트코드로 단단히 묶어놓으면

코드품질이 확 올라가는 서비스를 만들어낼 수 있을것이라 확신하며..

리팩토링과 추상화… 그리고 테스트코드는 계속 된다!

카테고리: 개발 | 태그: , | 답글 3개

[스타 프로리그 앱]새로운 기획에 대한 아이디어 끄적거리기

기존 앱의 주요기능은 e스포츠협회의 웹 페이지를 긁어와서 프로리그결과/팀, 개인랭킹

을 보여주는 것에 주안점을 두었다.

이번에 새롭게 리뉴얼을 하면서 재밌게 만들어볼 수 있는 기능들은 무엇인지 생각해보았다.

그리고 이번에는 설계부터 꼼꼼히 해볼 작정이다!

https://play.google.com/store/apps/details?id=com.starproleague

================================================================================

1. 스타크래프트와 연관된 검색어가 포함된 SNS글을 가져와 최신것만 리스트업 하는것, 내용 보내기

- 트위터, 페이스북, 구글플러스등..

- 내가 말하고 싶은 것을 해당 SNS로 보내는 것(응원트윗 같은거..)

- 내가 응원(즐겨찾기 기능 추가)하는 선수의 트윗만 모아서 보기

- e스포츠협회 트윗계정글을 공지사항으로 활용할수도 있을듯

2. e스포츠협회에서 가져오는 데이를 다양화하고 잘 가공하는 것

- 보도기사, 이벤트, 팀/선수 정보,

- 포모스 스타2기사 긁어와서 가공하기

3. 알림의 형태를 다양화하는 것(단체 => 개인화 형태)

- 내가 좋아하는 선수를 선택하고 그 선수의 경기가 있는 날 알림이 오기

- 내가 응원하는 팀이 1등으로 올라갔을 때 알림이 오기

- 매일 선수마다 SNS글을 통계내서 제일 많은 순서를 즐겨찾기한 대상자에게 알림 보내기

4.응원랭킹은 다른 방향으로 더 발전시켜야 할듯

5. 간단한 게임을 넣어서 당첨된 사람에게 “별”을 모아서 일정갯수 이상 모은 사람에게는

-  프리미엄 정보 제공하기

6. 프로리그 날짜별로 검색기능을 제공하는 달력을 업데이트 하기

- 경기가 없는 날은 선택이 안되게 하는 등.

카테고리: 개발 | 태그: , , | 댓글 남기기

[독후감]프로그래머로 산다는 것(프로그래머의 길을 생각한다)

가끔씩 프로그래머 선배님들의 이야기가 듣고 싶어 이런 부류의 책을 사곤 한다.

내가 가고자 하는 방향에 대한 물음과, 또 그 길을 어떻게 하면 올곧게 걸어갈 수 있을지에

대한 물음에 대한 해답을 찾기위해 책을 읽는 것인지도 모르겠다.

 

내가 이 책을 읽기 전 갈구했던 가장 큰 질문은 이것이었다.

“기본기 탄탄한 프로그래머가 되기 위해선 어떻게 해야할까?”

저 질문에 포커스를 두고 책을 읽다보니, 아무래도 그 외의 내용에는 관심이

떨어지는 것이 사실이었다.

다른 내용이 좋지 않다는 말은 아니다. 내 개인적인 생각으로는

하호진님과 김성박님의 글이 가장 마음에 와 닿았다.

특히 김성박님의 글에서 “진정한 프로그래머란 무엇인가“에 대한 답으로,

내가 무엇을 만들고 싶은 프로그램이 있는가?“, 그리고 “작은 것이라도 

나만의 작품을 만들어 오픈을 하는것이 중요하다“는 말이 기억에 남는다.

한가지 아쉬운 점은, 이런 부류의 책은 저자들의 프로그래머로서 히스토리가

나오는데, 그런 히스토리가 나올 때 단순히

이렇게 해서~이렇게 노력했더니~ 결과가 이렇게 좋았다“라는 식의 내용은

독자들의 마음에 크게 와닿지 않는것 같다.

히스토리를 쓰더라도, 모든 것을 다 나열하는데 그치는 게 아니라 그 안에서 했던

내용들 중에서 독자들에게 얘기하고 싶은 구체적인 예시와 과정들을

더욱 구체적으로 썼더라면 독자들에게 간접적인 지식을 전달해주는데

효과적이지 않을까 하는 개인적인 생각이다.

그리고, 이런 부류의 책도 선배개발자분들의 지식과 경험을 살려 서두에 말했던

프로그래머로 가져야 할 기본적인 소양과 지식, 경험“에 대해서 더욱 포커스를

맞추고 책이 나온다면, 선후배 개발자들에게 더욱 사랑받는 책들이 나오리라

생각한다.

카테고리: 독서, 일상과의 대화 | 태그: , | 댓글 남기기