일전에 Mesh생성하는 방법에 대해 써 놓은 적이 있는데,

Link : http://www.wolfpack.pe.kr/853

문득 Hexa로 만드는것도 가능하지 않을까? 고민했습니다.
그래서, 만들어 봤습니다.

먼저 Mesh 만든 절차를 그대로 따라 했습니다.
1. 좌표를 Vector3 array로 만든다.
* Mesh의 경우는 단 4개의 Vertex로 구성되면 되기 때문에, 좌표는 총 4개를 만들면 되죠.
예를 들어, pivot point가 Center에 있는 4각의 Mesh라면,
(-1, -1, 0), (-1, 1, 0), (1, 1, 0), (1, -1, 0) >> 각각 0, 1, 2, 3 번 이라고 합시다.

2. Polygon Array를 만듭니다. 하나 중요한건 Unity3D는 왼손 좌표계이므로 Surface가 -Z축에서 보여지려면 시계방향으로 Array를 생성해야 합니다.
* 0, 1, 2, 0, 2, 3 / 3개씩 잘라서 보면 0,1,2 폴리곤과 0, 2, 3 폴리곤 2개가 있어, 4각으로 보이죠.

자세한건 위의 Link 참조바랍니다.

Hexa의 경우는 간단히 다음과 같이 하면 되겠지하고 덤볐다가.. -_-;; 개피봤습니다.
사용자 삽입 이미지
1개 일때는 문제없이 0, 1, 3, 1, 2, 3, 0, 3, 4, 0, 4, 5 로 폴리곤 셋을 잘라주면 되는데.. 2개 이상만 되도 일정한 규칙을 발견하기 어려웠습니다.
사용자 삽입 이미지
슬슬 멘붕오기 시작하지요. 여기에 Y축까지 추가되면...
사용자 삽입 이미지
규칙은 사라지고 Chaos 만 남습니다. -_-;;
만약, Pivot이 Center에 있다면? 이라는 질문을 하게 되었고 Vetex를 정리해보니 다음과 같이 그런대로 규칙이 생깁니다.
사용자 삽입 이미지
정리하자면 2*2짜리 Hexa는 가로로 n개의 Vertex를 가집니다.
이걸 수식으로 정리하면 Hexa갯수가 x라면 Vertex수는
1 - 3
2 - 5
3 - 7
4 - 9
n - 2 * 헥사갯수 + 1

입니다만, Hexa는 항상 위에서 보는바와 같이 왔다 갔다 하므로 Y축 확장을 위해 우로 Vertex열이 1줄 더 있어야 하고 좌로도 1줄 더 있어야 하는 상황이 됩니다.

그리고, 또 하나 만난 문제는 저렇게 Vertex를 배열하는게 문제였습니다.
6각형은 60도로 나누어져 있는데 소숫점 오차로 인해 제대로 배열이 안되는것도 문제여서...
그냥 다음과 같이 간단히 일렬로 배열한뒤에...
사용자 삽입 이미지
홀수 열만 0.5정도 올려주면...
사용자 삽입 이미지
근사하게 육각으로 바뀌는 군욤. ㅋ

* 여기까지 코드는 다음과 같습니다.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Hexa : MonoBehaviour {

    private Vector3[] vertexs; 

    void Awake()
    {
        gameObject.AddComponent<MeshFilter>();
        gameObject.AddComponent<MeshRenderer>();
    }

    void Start () {
        SetVertex(5, 6);
    }
    
    void SetVertex(int xSize, int ySize)
    {
        //사전처리 부분입니다. 예외처리를 위해 가로 헥사수는 2이상, 세로 헥사수는 짝수로 고정합니다.
        if (xSize < 2) xSize++; //가로 헥사수, 예외 처리를 위해 2이상으로 강제 처리
        if (ySize % 2 == 1) ySize++; //세로 헥사수  //예외 처리를 위해 짝수로 강제 처리

        // 가로와 세로의 버텍스 수를 구합니다.
        int xDotSize = 2 * xSize + 2; //가로 버텍스 수
        int yDotSize = (ySize % 2 == 0) ? (3 * ySize / 2) + 1 : 3 * (ySize / 2 + 1); //세로 버텍스 수

        //For 문으로 자동으로 찍되 홀수 줄은 0.5f만큼 올립니다.
        MeshFilter MF = GetComponent<MeshFilter>();
        vertexs = new Vector3[xDotSize * yDotSize];
        for (int i = 0, y = 0; y > yDotSize * -1 ; y--)
        {
            for (int x = 0; x < xDotSize; x++, i++)
            {
                vertexs[i] = (x % 2 == 0) ? new Vector3(x, 0f, y) : new Vector3(x, 0f, y + 0.5f);
            }
        }
        MF.mesh.vertices = vertexs;
    }

    private void OnDrawGizmos()
    {
        if (vertexs == null) return;
        Gizmos.color = Color.red;
        for (int i = 0; i < vertexs.Length; i++)
            Gizmos.DrawSphere(vertexs[i], 0.1f);
    }
}


이제 문제는 Center를 찾는 겁니다. 의외로 어렵게 해결된 부분인데,
먼저 좌변의 Center Point들을 먼저 찾고 거기에 가로로 +2를 반복하였습니다.
그 이유는 다음의 그림에서 처럼 첫 Center는 2번째, 2번째 Center는 5번째... 이런식으로 나타 났고 이를 위해 수열 공식 찾느라 오래 걸렸습니다.
사용자 삽입 이미지
암튼 위의 결과를 보인 소스는 다음과 같습니다.

        //SetVertex() 함수 밑부분에 넣었습니다.
        //Find Center
        CenterP = new Vector3[xSize * ySize];
        for (int idx =0, y = 0; y < ySize; y++) {
            int verticesIdx = 0;
            verticesIdx = xDotSize * ((y / 2) + 1 + y) + 1 + (y % 2); //버텍스인덱스는 가로크기* ((y / 2) + 1 + y) + 1 + (y % 2), y는 세로변수 y
            CenterP[idx] = vertexs[verticesIdx];
            CenterIdx.Add(verticesIdx);
            idx++;
            for (int x = 1; x < xSize; x++)
            {
                CenterP[idx] = vertexs[verticesIdx + 2 * x]; //세로에서 구한 센터값에 +2 반복
                CenterIdx.Add(verticesIdx + 2 * x);
                idx++;
            }
        }

좀 복잡하지만, CenterP는 OnGizmo에서 디버깅 용으로 만든 변수이고, 실재 사용하는 변수명은 CenterIdx입니다.
클래스 변수는 다음과 같이 위의 2개 변수를 추가했구요.

    private Vector3[] vertexs;
    private Vector3[] CenterP;
    private List<int> CenterIdx = new List<int>();


마지막으로 OnGizmo를 다음과 같은 최종형태로 만들었습니다.
    private void OnDrawGizmos()
    {
        if (vertexs == null) return;
        Gizmos.color = Color.red;
        for (int i = 0; i < vertexs.Length; i++)
            Gizmos.DrawSphere(vertexs[i], 0.1f);

        if (CenterP == null) return;
        Gizmos.color = Color.yellow;
        for (int i = 0; i < CenterP.Length; i ++ )
            Gizmos.DrawSphere(CenterP[i], 0.1f);

    }


마지막으로 Vertex 연결 순서는 짝수 열과 홀수 열이 조금 다르지만, 정리하면,

                //총 6개의 폴리곤이 있어야 6각형이 나오므로...
                triangles[idx] = CenterIdx[x];
                triangles[idx + 1] = CenterIdx[x] - 1;
                triangles[idx + 2] = CenterIdx[x] - xDotSize;
                
                triangles[idx + 3] = CenterIdx[x];
                triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = CenterIdx[x] + 1;
                
                triangles[idx + 6] = CenterIdx[x];
                triangles[idx + 7] = CenterIdx[x] + 1;
                triangles[idx + 8] = CenterIdx[x] + xDotSize + 1;

                triangles[idx + 9] = CenterIdx[x];
                triangles[idx + 10] = CenterIdx[x] + xDotSize + 1;
                triangles[idx + 11] = CenterIdx[x] + xDotSize;
                
                triangles[idx + 12] = CenterIdx[x];
                triangles[idx + 13] = CenterIdx[x] + xDotSize;
                triangles[idx + 14] = CenterIdx[x] + xDotSize - 1;
                
                triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 16] = CenterIdx[x] + xDotSize - 1;
                triangles[idx + 17] = CenterIdx[x] - 1;

위의 코드를 좀 짧게 축약하면 다음과 같습니다.

                triangles[idx] = triangles[idx + 3] = triangles[idx + 6] = triangles[idx + 9] = triangles[idx + 12] = triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 1] = triangles[idx + 17] = CenterIdx[x] - 1;
                triangles[idx + 2] = triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = triangles[idx + 7] = CenterIdx[x] + 1;
                triangles[idx + 8] = triangles[idx + 10] = CenterIdx[x] + xDotSize + 1;
                triangles[idx + 11] = triangles[idx + 13] =  CenterIdx[x] + xDotSize;
                triangles[idx + 14] = triangles[idx + 16] = CenterIdx[x] + xDotSize - 1;


이제 이걸 돌려 보면... 두둥..
사용자 삽입 이미지
전체 코드는 다음과 같습니다.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Hexa : MonoBehaviour {

    private Vector3[] vertexs;
    private Vector3[] CenterP;
    private List<int> CenterIdx = new List<int>();

    void Awake()
    {
        gameObject.AddComponent<MeshFilter>();
        gameObject.AddComponent<MeshRenderer>();
    }

    // Use this for initialization
    void Start () {
        SetVertex(5, 6);
    }
    
    void SetVertex(int xSize, int ySize)
    {
        if (xSize < 2) xSize++; //x 헥사수
        if (ySize % 2 == 1) ySize++; //y 헥사수
        int xDotSize = 2 * xSize + 2; //가로 버텍스 수
        int yDotSize = (ySize % 2 == 0) ? (3 * ySize / 2) + 1 : 3 * (ySize / 2 + 1); //세로 버텍스 수
        MeshFilter MF = GetComponent<MeshFilter>();
        vertexs = new Vector3[xDotSize * yDotSize];
        for (int i = 0, y = 0; y > yDotSize * -1 ; y--)
        {
            for (int x = 0; x < xDotSize; x++, i++)
            {
                vertexs[i] = (x % 2 == 0) ? new Vector3(x, 0f, y) : new Vector3(x, 0f, y + 0.5f);
            }
        }
        MF.mesh.vertices = vertexs;

        //Find Center
        CenterP = new Vector3[xSize * ySize];
        for (int idx =0, y = 0; y < ySize; y++) {
            int verticesIdx = 0;
            verticesIdx = xDotSize * ((y / 2) + 1 + y) + 1 + (y % 2);
            CenterP[idx] = vertexs[verticesIdx];
            CenterIdx.Add(verticesIdx);
            idx++;
            for (int x = 1; x < xSize; x++)
            {
                CenterP[idx] = vertexs[verticesIdx + 2 * x];
                CenterIdx.Add(verticesIdx + 2 * x);
                idx++;
            }
        }

        int[] triangles = new int[18 * (xSize * ySize)];

        for (int idx = 0, x = 0; x < CenterIdx.Count; x++)
        {
            if ((x / xSize) % 2 == 0)
            {
                triangles[idx] = CenterIdx[x];
                triangles[idx + 1] = CenterIdx[x] - xDotSize - 1;
                triangles[idx + 2] = CenterIdx[x] - xDotSize;

                triangles[idx + 3] = CenterIdx[x];
                triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = CenterIdx[x] - xDotSize + 1;

                triangles[idx + 6] = CenterIdx[x];
                triangles[idx + 7] = CenterIdx[x] - xDotSize + 1;
                triangles[idx + 8] = CenterIdx[x] + 1;

                triangles[idx + 9] = CenterIdx[x];
                triangles[idx + 10] = CenterIdx[x] + 1;
                triangles[idx + 11] = CenterIdx[x] + xDotSize;

                triangles[idx + 12] = CenterIdx[x];
                triangles[idx + 13] = CenterIdx[x] + xDotSize;
                triangles[idx + 14] = CenterIdx[x] - 1;

                triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 16] = CenterIdx[x] - 1;
                triangles[idx + 17] = CenterIdx[x] - xDotSize - 1;

            }
            else
            {
                triangles[idx] = CenterIdx[x];
                triangles[idx + 1] = CenterIdx[x] - 1;
                triangles[idx + 2] = CenterIdx[x] - xDotSize;
                
                triangles[idx + 3] = CenterIdx[x];
                triangles[idx + 4] = CenterIdx[x] - xDotSize;
                triangles[idx + 5] = CenterIdx[x] + 1;
                
                triangles[idx + 6] = CenterIdx[x];
                triangles[idx + 7] = CenterIdx[x] + 1;
                triangles[idx + 8] = CenterIdx[x] + xDotSize + 1;

                triangles[idx + 9] = CenterIdx[x];
                triangles[idx + 10] = CenterIdx[x] + xDotSize + 1;
                triangles[idx + 11] = CenterIdx[x] + xDotSize;
                
                triangles[idx + 12] = CenterIdx[x];
                triangles[idx + 13] = CenterIdx[x] + xDotSize;
                triangles[idx + 14] = CenterIdx[x] + xDotSize - 1;
                
                triangles[idx + 15] = CenterIdx[x];
                triangles[idx + 16] = CenterIdx[x] + xDotSize - 1;
                triangles[idx + 17] = CenterIdx[x] - 1;
            }
            idx += 18;
        }

        MF.mesh.triangles = triangles;
        MF.mesh.RecalculateNormals();

    }

    private void OnDrawGizmos()
    {
        if (vertexs == null) return;
        Gizmos.color = Color.red;
        for (int i = 0; i < vertexs.Length; i++)
            Gizmos.DrawSphere(vertexs[i], 0.1f);

        if (CenterP == null) return;
        Gizmos.color = Color.yellow;
        for (int i = 0; i < CenterP.Length; i ++ )
            Gizmos.DrawSphere(CenterP[i], 0.1f);

    }
}


그럼~ 새해 복 많이 받으세요!
2016/02/09 17:17 2016/02/09 17:17
한 사람의 개발자로써 링고게임즈가 드디어 게임다운 게임을 낸다는데 만족하고 있습니다.
개발기간은 그래픽/기획까지 2주, 서버작업과 클라이언트 작업 2주 총 4주가 걸렸어요.

그래픽 이팩트를 제외하고는 거의 모든 기능은 다 들어 가 있으며, 1차 Feedback후에 장르를 조금 바꾸어서 아마도 업계 최초의 모바일 SNG+AOS로 탄생할 예정입니다.

많은 분들이 축하해주셨습니다. 대체적인 반응은 이러합니다.

"어떻게 이렇게 빨리 개발했는가?"

하지만, 우리는 이게 빠르다고 생각하지 않습니다.
늦었다고 생각합니다.

Zynga의 경우는 겨우 6주만에 게임을 하나 씩 찍어냅니다.
물론 그쪽 인력은 40명에 가깝고, 저희는 자사인력 저포함 3명에 합작사 인력 4명으로 7명밖에 안되는 인력이지만,
그들과 경쟁하려면 8주 정도면 게임 하나가 출시되어야 합니다.

3D라서 더 어려운 부분이 있겠죠...

각설하고 저희 개발팀의 초고속 개발 비밀을 공개하려 합니다.

그 비밀은 "실패"에 있습니다.

남벌 SNG를 공개한 장면을 자세히 보시면 Cojndozer 게임이 들어가 있는데, 이는 작년에 처음 게임으로 만들었던 게임입니다.
물리 엔진을 테스트할 겸 Facebook 시장상황을 알아볼겸 FB에 올렸다가 망한 넘입니다. =)

그리고, 액션 슈팅 요소는 SEGA에서 컨번전 의뢰했던 모 게임의 Prototype입니다.
처음 이야기와는 달리 리소스를 처음부터 끝까지 다 만들어야 해서 결국 포기했었던 녀석입니다.

그외에는 서버를 만든다든가하는 일은 전공이라서 ASP.NET의 MVC3와 Linq로 2주간 병행작업하며 만들었구요.
건설 쪽과 퀘스트 쪽은 그런대로 쉬운 편이라 후딱 해치웠습니다.

그런데 거의 대부분의 모듈이 이미 전작에서 실패했던 게임에서 빼온 겁니다.

가령 터치나 줌인 줌아웃 등의 기본 제스쳐 인식이라던가...
건물을 지을때 따라다니는 icon 이라던가...
충돌 체크를 한다던가...
등등의 모듈들을 이미 실패한 제품에서 뽑아서 재활용 했습니다.

아마도 새로 개발하고 해야 할 부분이 많아지겠지만, 리얼타임 서버는 이미 테스트를 끝내놔서 소스를 리팩토링하고 붙이면 될 듯 싶어요.

서버 통신 모듈은 아직 리팩토링이 안되어 있어서 복잡한데..
(Structure에 List로 쳐발라 놓은데다가 공통 부분을 빼놓질 않아서리.. -_-;;)
이 부분도 다음주 정도에 모듈화 시켜 놓을 생각입니다.

실패를 실패로 인식하느냐? 실패를 도전의 결과물로 인식하느냐?는 이 처럼 팀의 생산성에 크게 영향을 줍니다.

그걸 증명해 보여서 기쁩니다. =)

이 글을 보시는 여러분들은 어떻게 생각하세요?

(참고로 2주간의 철야는 제가 전담했고 나머지 인력들은 로테이션하면서 도와주었습니다. 평소 근무시간이 6.5시간이고, 토마토 기법을 활용하여 업무 집중 시간을 적용해본 결과 3~4.5시간 정도를 집중도 있게 일하면서 이 정도의 결과물을 만들어 냈습니다.)
2012/09/23 02:06 2012/09/23 02:06
김**님은 차주에 요청하셔서 다음 주 정도에 발송드릴께요.
나머지 분들은 다음과 같이 발송드렸습니다. 착불요금 4,500원입니다.

윤**님 등기번호 : 74195-0500-5372
박*님 등기번호 : 74195-0500-5373
이**님 등기번호 : 74195-0500-5374
홍**님 등기번호 : 74195-0500-5375

행여 오탈자나 오역으로 잘 이해가 안되시는 부분이 있으시다면 연락주시면 번역할때의 뉘앙스를 알려 드릴 수 있을 것 같습니다.

* 출판사에서 제공하셨던 원본을 회수하셔서... 원래 원문을 알려드릴려고 했던 의도는 달성 못할 것 같아요.
양해부탁드립니다.

* 어제 모회사의 지인분께 드렸더니 "앗! 레어다!" 하시더군요. 30부만 만들어진 허접떼기 레어 아이템 맞습니다. ㅋ
2011/11/04 15:05 2011/11/04 15:05
http://sungmooncho.com/2011/08/29/software-korea/

꼭 읽어 보십시오!

정통부 부활이 얼마나 어처구니 없는 주장인지 insight를 얻으시기 바랍니다.

이제는 기업이 기업 스스로 먹고 살길 찾아야 하며, 정부에 기대거나 대기업에 기대는 그런 나쁜 버릇을 제거해야 합니다.
벤쳐도 스스로 고객을 만들어야 한다는 이야기로 국내 시장이 좁다면 나갑시다. 밖으로.

밖은 해볼만 합니다.

그곳에는 국내 왠간한 대기업이나 벤쳐나 똑같은 듣보잡 취급받는 동네입니다.
이런 환경이라면 벤쳐도 밖에서 뭔가 해볼 수 있지 않겠습니까?
2011/09/06 17:54 2011/09/06 17:54
장하준 교수의 "나쁜 사마리안"이라는 책을 보면 (물론, 이 책은 낮잠 트리거용으로 딱이다.아직까지 3페이지 이상을 연속해서 읽어본적은 없다. 읽다가 잠이 와서 도저히 읽을 수 없었지만 3/4정도 지날 무렵 드디어 포기한 책이다. 작은 글씨체에 반복되는 이야기에... OTL...교수님! 죄송합니다.) "사다리 걷어차기"라는 용어가 등장한다.

쉽게 풀이하면 이러하다.
"소위 유럽 선진국 쉬팍들은 말야, 지네가 어떻게 성장했는지 잘 알고 있어서 후진국들이 지네 따라 할려면 그거 못하게 하려고 '사다리 차기'라는 비열한 짓거리를 하거든~~ 어쩌구 저쩌구..."

그런데 웃기게도 안드로이드 진형에서 이런 일이 벌어지고 있다.

첫타자는 Microsoft사.
완존 무료인 안드로이드 OS는 실제로 Microsoft사의 기술을 대거 차용하고 있다나?
(하긴, Windows CE로 소비자들 물먹인게 몇해냐?)
삼성 뿐만 아니라, 대다수의 안드로이드 벤더들은 MS에게 대당 20~30불 정도의 비용을 지불해야 한단다.
(웃긴건 Google이 책임이 없다니? 폰용 OS야 그렇다 치고 SmartTV는 비용 꼬박 꼬박 받아가고 스팩을 통제하는 구글은 전혀 책임 없음? ㅡㅡ?)

거기다가, 썬을 인수한 오라클에서도 "자바"사용료 내란다.

이런 어처구니 없는 경우가 다 있을까?

왜냐하면 C#이 처음 나왔을 때 수 많은 자바 전문가들이 존나 씹어대면서 특정 회사의 기술과 언어를 사용하면 반드시 댓가를 치루게 될 것이라고 경고하며 MS의 .NET Framework를 존나 씹어 댔던게 5~6년전의 일이다.

그러나, 결과적으로 어떠한가?
자바진형이야 말로 그 경고를 실행에 옮기고 있지 않은가?

아무튼 오라클도 20~30불정도 받아 갈려고 한다니, 폰 1대장 40~60불정도의 저렴한 로열티를 소비자는 더 물게 됐다.
(제조사에 머리에 총맞은 것도 아니고, 지네가 부담하겠는가? 다 소비자 전가라는 자연스러운 수단이 있는데...)

그렇다고 HTC나 삼성에서 안드로이드를 버릴 것 같지는 않다.
이미 깔아놓은 마케팅비용에 포팅 기술이 있는데... 아니면 MS에 "니미~, WM7 몇 백만대 보증해줄께!" 이럴까?, 아니면 갑작이 어려운 Linux OS 채용 이럴까?

이럴때는 소니나 Nintendo가 부럽다.
OS를 가지고 있는데다가 이미 모바일 기기 만드는건 도가 튼 넘들이니까, 전화 기능 하나 더 넣는게 어려운 일도 아닐터...

뭐~ 암튼,  2D 게임 개발 벤쳐사의 대표입장에서는 어느 놈이든 이기는 넘이 장땡이다.
너무 많은 플랫폼을 다 익혀야 한다는 건 결국 원가 상승으로 밖에 이어질 수 없기 때문에, 어느 놈이든 한넘만 이겨라.
 
2011/07/11 16:53 2011/07/11 16:53
안철수 교수님이 11월 16일 서울 프라자 호텔에서 '2010 대한민국 모바일앱 개발자 컨퍼런스' 기조연설중에 한마디 하신 말씀이 가슴에 와 닿는다.
(Source : blog.naver.com/ckddmlgurtls/40117931167 )

"지난 30년동안 창업회사 중 매출 1조원 이상의 대기업으로 성장한 회사는 웅진과 NHN뿐이고 대기업에 납품한 기업 중에서는 하나도 없다"

대기업에 납품하면 성장에 한계를 겪게 마련이다.
이유는 간단하다 인력 장사의 원가 및 매출액이 유리알처럼 투명하기 때문이다.
그럼에도 SI라는 미명하에 인력장사는 계속 된다. Function Point에 기대를 걸었지만 최근의 업계는 가격에 FP를 때려 맞추는 일이 일어나고 있다. (뜨어...)

이래서 이 바닥이 싫다는 것이다.

MS도 최초에는 MITS, Apple, IBM 등에 S/W를 납품하던 회사로 출발하였지만 스스로의 왕국을 건설하고 이를 바탕으로 비즈니스를 계속 해나갔다.
그러나 MS가 MITS, Apple에 안주했다면 어떻게 되었을까?

그들은 거기에 안주하지 않았고 더 어려운 길을 선택하게 된다.
스스로의 OS를 만들고 개량해갔고 스스로 고객을 만들었으며 일반 대중에게 물건을 팔아 치웠다.
즉, 스스로 물고기 잡는 법을 터득하고 스스로 물고기를 잡았지 누구에게 물고기를 잡아 달라고 하지 않았던 것이다.

이는 어느 정도 시장이 성장함에 따라 나타나는 현상중 하나이다.
일례로 최근 통신사에 BP로 있던 회사들의 행보만 보아도 알 수 있다.
더 이상 통신사의 Sida 생활을 하기를 거부하고 스스로 이제는 먹고 살수 있다는 생각을 한다.
COM2US 만 보더라도 이제는 더이상 통신사 쫒아 다니며 영업하지 않는다.
그들 스스로 선택한 플랫폼에 그들 스스로 선택한 이통망위에서 비즈니스를 하는 것이다.
물론 그들이 가지고 있는 약점은 분명하다.

모바일 케쥬얼에만 집착하고 있기 때문에 조만간 모바일에 메이저 플레이어 들이 들어오면 경쟁력이 약화될 것이 분명하기 때문이다.
(어차피 인생자체가 자전거 타고 오르막 내리막 해야 할 상황이라면 저런 준비를 해야 하는 입장에서 여러가지의 사고 나 케이스 스타디는 분명 도움이 된다.)

결론은 어려운 길을 선택하는 것이 정답일 수 있다는 생각이 든다.

어렵지만 스스로 영업망을 넓히면서 스스로 무엇인가 만들어 팔아야 제대로 된 장사이며 사업이다.
일부 SI회사들이 욕먹는 부분이 바로 이 부분일 것이다.
영업망은 있으되 스스로 무엇인가 만들어 파는 부분이 없다.
이 부분도 분명히 국가 정책 및 회사의 정책과 밀접한 관련이 있다.
몇 백종이나 되는 산출물을 만들라고 하는 것 부터 가격 제한 정책을 쓰는 것까지 온갖 규제는 다 갖다 붙이니 개발자를 유지할 능력이 공룡에게는 없다. 그야말로 1차 산출물인 프로그램이 아닌 2차 산출물인 문서만 양산하는 조직이 된지 오래다.
즉, 머리만 큰 뚱뚱한 공룡에 불과하여 근육이 붙어 있는 것이 없어  뛸 수 있을 때 뛰지 못하고 있는 현실이다.
마치 인간이 자신의 팔과 다리를 사용하지 않고 기계에 의존함에 따라 점차 머리만 발달하고 손과 발은 퇴화되고 몸은 점점 비대해지는 모양이라고 할까?

* Wall-E에서 우리는 익숙한 장면을 보게된다.
사용자 삽입 이미지

결국 살아 남은 머리는 계속 회전하면서 불사의 세포가 되는데 이게 바로 암이라는 존재다.
현재 대한민국의 SI업계는 바로 암에 걸려 있는 상태이다.

건강하게 다시 살아나기 위해서는 위의 이미지에서 저 뚱뚱한 인간이 앉아 있는 "병"이라는 의자를 걷어 차야 한다.
그리고 무한의 에너지를 공급하는 저 시스템 자체가 파괴되어야 한다.

즉, 국가에서 제한하는 가격 정책 자체와 합리화 방안등이 사라져야 한다는 이야기이다.
그래서 시장에 맡겨 버려서 죽을 업체는 죽고 살 업체는 살아야 한다.
억지로 키워진 공룡이 결국 주변의 모든 것을 다 빨아 들이고 나면 그것은 질량의 가중만 거듭하다가 어떤 충격을 받으면 곧 중력에 영향을 주고 주변 사물의 모든 것을 빨아 당겨서 같이 공멸하는 블랙홀이 될 뿐이기 때문이다.

대신 국가는 가격 또는 품질에 대한 제한을 풀고 시장 경쟁을 시키면서 해줘야 할 것이 있다.
바로 지적권에 대한 감시 및 처벌 강화와 더불어 해외에 나가는 SI업계에 대한 지원이다.
이를 통해 비정상적으로 비대해진 SI업계를 다이어트 시키고 건강하게 해외나가서 붙어볼 만한 대표선수를 만들 수 있는 것이다.

이상 몇개월뒤 30대의 도전을 시작하려는 나에게는 이런 환경이 얼마나 좋을까 하는 생각 빡에 없지만.. ㅋㅋㅋ
2010/11/22 17:13 2010/11/22 17:13

월요일 새벽 4시.
현재 월요일 오후 고객사 사업팀장님 시연때문에 금욜 오후부터 토욜, 일욜 (특히 오늘은 철야모드) 버닝중이다.
문득 버닝중에 과연 철야 또는 야근이 절대악인지를 묻고 싶어 졌다.

예전의 내 포스팅을 보면 야근에 관련된 이야기가 많이 나온다.
애자일 용어로 Sprint 즉, 스스로 치열하게 일하는 것에 대한 이야기가 많이 나오고 있다.

그럼 꺼꾸로 한번 스스로에게 되물어 보도록 하자.
철야 또는 야근은 절대 악인가?
그리고 프로젝트 기간동안 제일 기억남았던 좋던가 싫은 기억은 무엇인가?

대부분의 개발자는 야근, 철야, 주말근무를 떠올릴 것이다.

그럼 우리가 알고 있는 야근, 철야, 주말근무는 왜 나쁜것일까?

첫째, 개발자 스스로의 건강을 깨트린다. 나역시 아침에 일어나기 힘들고 주말에는 퍼지기 일수이다.
거기다 고칼로리 야식으로 인해 살이 비둥비둥 쪄간다.
둘째, 능률 저하가 발생한다. 어차피 야근하는 문화라면 뭐하러 근무시간내에 일을 마치려 바둥바둥 하겠는가?
어차피 야근이라면 근무시간에 일에 집중하기보다 놀기바빠지기 마련이다. 그리고 이런게 습관화 되면 습관으로 인해 스스로의 인생조차 장담 할 수 없게 된다.

즉, 상시 야근 및 철야, 주말근무는 결국 개발자 나아가 개발팀 더나아가 회사의 장기적 손해를 끼친다.
그래서 나쁜것이 바로 야근, 철야, 주말근무인 것이다.

그럼 왜 이런 좋게 말해 필요악이 발생하는가?
가장 큰 이유는 일정이다.
일정 계획이 무계획, 무기준으로 짜여져서 결국 프로젝트를 지옥으로 빠트리는 상황이 가장 많을 것이다.
그다음이 원가 절감이다.
갑또는 을의 원가 절감으로 인해 절대적 업무량이 많은 경우이다.

그런데 야근, 철야, 주말근무가 반드시 나쁜것일까?
내 생각에는 리더인 PM이 직접 하는 야근, 철야, 주말근무는 필요악이다. 스스로 희생양으로 삼아 고객에게 좋은 점수를 딴다면 그가 이끌고 있는 팀은 어찌됐건 차기 프로젝트를 수주할 확률이 올라간다.
문제는 무능력한 PM이 이끌고 있는 팀은 PM스스로 업무를 처리할 수 없기에 물귀신 처럼 밑에 팀원들 데리고 자폭하는데 그 문제의 심각성이 있다.

이제 야근의 장점을 이야기해보자.
야근을 함으로써 얻을 수 있는 것은 팀원간의 친밀도와 팀웤이다.
짧고 강렬한 경험을 공유하면 그 경험으로 인해 팀원의 팀웤을 일시적으로 상승시킬수 있다.
군에서 고생을 같이 하고나면 전우애가 생기듯이 사회도 똑같은 원리로 이러한 효과를 기대할 수 있는 것이다.

현명한 PM이라면 이를 적절하게 활용해야만 한다.
모든 품질은 정량화에서 부터 시작된다는 이야기를 많이 하였다. 업무가 70% 정량화되어 팀원들에게 할당되면 70% 예측이 가능할 것이다. 즉, 업무를 세부적인 메소드 수준까지 정량화시키고 이를 팀원들에게 할당하면 업무의 우선순위를 정하고 일정기간 그것을 완성하기위해 Sprint하고 그 보상으로 1~2일의 휴가 또는 오후 3시 이전에 조기 퇴근할 수 있는 권한을 준다면 업무의 효율성을 재고할 수 있다.
이건 경험적인 것으로 이러한 보상을 통해 팀웤을 끌어올리면서 업무 효율성을 재고하는 2마리의 토끼를 다 잡을 수 있는 기회이기도 하다.

따라서, 무조건 절대 악이라는 것도 절대 선이라는 것도 없다는 것이다.
최선은 그 사이에 있기 마련인 것이다.

정리하자면
1. 야근, 철야, 주말근무해야 한다면 PM이 직접하라. (리더일수록 팀원에 비해 근무시간이 더 늘어나는것은 당연하다)
2. Sprint 기간을 설정하고 Sprint한 후 그 몇배의 시간 만큼은 정시 출퇴근 혹은 쉴 수 있는 시간을 부여하라.
3. 현재 팀원이라면 자신의 환경적, 신체적, 가정 컨디션을 팀 리더에게 수시로 알려라. (최소한 이메일정도는 남겨야 한다)
4. 이유없는 야근은 하더라도 2주 혹은 3주에 한번 정도로 제한하고 어쩔수 없이 야근하는 경우는 그 자체를 즐겨라.
5. 왠만하면 퇴근할 시간에는 집으로가서 재택을 하자.
가 되겠다.

2010/08/30 04:18 2010/08/30 04:18

프로젝트 근황...

Memory 2010/05/14 00:26

팀 빌딩에 실패하고 있는 상황이다.

2Weeks 단위의 Milestone으로 관리되고 있을때는 목표에 달성하는 비율이 매우 높았지만...
현재는 최악의 경우가 발생한다.

목표에 대한 관리 시간이 없는것이 가장 큰문제.

1. 목표 하달후 단기간에 목표 달성 파악을 위한 데모를 요청
2. 허겁지겁 하달된 목표 달성
3. 품질 저하
4. 신뢰하락과 함께 최초 하달된 목표보다 짧은 시간내에 목표 달성 요청
5. 허겁지겁 하달된 목표 달성
6. 품질 저하
.
.
.

현재 무한반복중.

짧은 일정도 문제지만 현재 정신 없는 부분은 당췌 어디서 부터 이렇게 팀웤이 무너져 버렸는지 파악조차 안된다는 것.
결국 땜빵식 업무 추진이 일어나고 있고 가장 하지 말아야 할 과거로의 회귀로 귀결되고 있는 상황.
차주 데모 시연만 3일이 잡혀 있는 상황에서 더이상 어떻게 잡아 볼 도리도 없는 상황.

현장 PM으로써 너무 우울한 하루이다.

거기다가 오늘 현장자리를 비운 탓에 챙기고 있던 요원도 없는 상태였고 PL혼자 챙기는 상황에서 정신없이 자신의 하달된 목표 달성에 급급했던것이 문제인듯 하다.

즉, 프로젝트 범위중에 어느 한 범위를 다시 빌딩하려 자리를 비우면 비운곳이 구멍이 나는 상태...
모든 업무를 중지하고 휴가 보내거나 팀 단합대회라도 하지 않으면 안되는 상황이 된듯 하다.

위기...
그리고 분노와 슬픔...

1달여간 잘 달려오던 팀이 일순간에 단 2주만에 이런 꼴이 된게 화가나고 슬프고 짜증나고
그냥 혼자 고생해도 될 여러사람 고생시키는 것 같아 더 안타깝다.

스스로의 능력에 대한 의심과 회의...
어찌해야 하나 하는 생각이 든다.

현재 계획은 계약이후로 미룰 생각이었으나 우리 요원들만이라도  매일 아침 10시에 정모하는 방법 밖에 없을듯.
하루 일을 정리하고 예상되는 Risk 파악하는 회의를 매일 30분씩 가져야 할듯 하다.

2010/05/14 00:26 2010/05/14 00:26
오늘은 너무 훌륭한 기사를 발견하여 포스팅하는데 의미를 두고 11-1편이라 이름지었다.
국방일보의 국방과학연구소에서 K-200 장갑차를 만들당시의 기사에서 발췌하였다.
“선행개발 단계의 기술시험과 운용시험에서 우리는 2000여 항목 이상의 결함을 발견해야 한다고 각오하고 시험을 실시했다.”

 시험평가를 주관하는 군의 목소리가 아니었다. 연구개발(R&D)을 주도한 국방과학연구소(ADD) 연구원들의 톤 높은 목소리였다. 시험평가란 구성품을 잘 제작해 제대로 종합했는지, 그 과정과 결과의 잘잘못을 따지는 절차는 아니다. 연구개발 단계에서 발견되는 결함이나 오류는 정식 생산 단계 즉, 양산을 통해 나온 최종 제품의 그것이 아니므로 해결만 되면 문제될 것이 없다. 오히려 시험평가를 통해 결함을 많이 찾아 해결할수록 최종 제품의 완성도는 높아지는 것이다.

 ‘한국형 장갑차’의 선행 시험평가에서는 204개 항목의 결함을 발견해 해결했다. 이 2000여 항목의 결함 발견 의지와 발견한 204개의 결함! “장갑차 모델 이름을 왜 ‘K200’이라고 지었을까?”라는 의아심이 든다면 그 답은 여기에서 찾아야 한다. 200은 바로 결함 발견 목표 2000개를 상징하고 있는 것이다.

나는 애자일을 방법론이라 정하지 않는다.
애자일은 개발 철학이며 그 중심에는 "회고"라고 하는 일련의 "성과평가"와 TDD 즉, 자동화된 Unit TEST를 통한 반복개발이 자리잡고 있다.

현재 내가 리딩하고 있는 개발조직 역시 SVN으로 소스 형상관리 정도에 만족하고 있는 조직이었고 이러한 개념을 이해시키고 시간예측과 품질예측, 요구사항 형상관리 부분에 있어서는 개발자 스스로 거부하는 조직이다.
이러한 조직에 맞춤으로 리딩할 필요성은 언제나 상존한다.

그럼에도 TDD를 포기하기 힘든 이유는 바로 국방과학 연구소에서 1984년에 (무려 26년전에) 저러한 모토로 개발에 임했다는 것이다.

개발자가 개발과정에서 오류를 범하는 것은 실력과 관계없는데도 불구하고 오류가 사전에 들어나는것을 부끄러워 한다는 것이 Mind의 문제 그리고 가장 중요한 "질책"이라는 책임의 문제이기 때문일까?

Software개발자들은 우리 나라에서 그것도 26년전에 했던 것을 못한다하면 무엇이 문제일까?
2010/03/30 12:24 2010/03/30 12:24

한 몇년전부터 UX(User eXperience)가 화두가 되었다.
마침 미국쪽 다시말해 UX라는 개념을 소프트웨어 개발 산업에 적용했던 그 사람들과 간접적으로 일한적이 있었고 거기서 나온 산출물을 뜯어볼 기회가 있었다.

결론적으로 충격이었다.
대부분의 한국쪽 UX전문가가 Rich Client 즉, 기술적 접근을 하고 있을때
양키넘들은 유저가 겪게 되는 경험에 대해 이야기 하며 약간은 철학적 접근을 하고 있었다.

그속에 기술이라는 것은 없었다.
단지 기술을 담을수 있는 푸대(?)를 만들어 놓고 접근하고 있었다.

실례를 들어보면
UX전문가라고 하시는 분들을 만나서 인터뷰할때는 이론상에 있는 모습을 보여주지만 실제 산출물을 보면 UX 설계서라는 예전의 UI설계서와 다를바 없는 산출물을 내놓는다.

그러나 본토의 산출물을 보니 "User Story Telling" 을 거쳐 "User에게 줄수 있는 가치"를 찾아내는 과정이 UX설계 과정이었다.
즉, 이전에 UI설계나 Screen 설계도 나름대로의 의미를 가지고 진행된다.

먼저 USER Strory가 작성된다.
가상으로 USER가 우리의 Product를 이용할때 얻을 려는 가치를 가상의 Storyline을 설정하는 것으로 Baseline이 되는 가상의 소설이 작성된다.
유저가 가입할때부터 활동, 탈퇴 또는 다른 친구에게 추천 등등등 모든 경우수가 적용된다.
Story가 정해지면 몇가지 시나리오별 화면 UI가 도출된다.
UI의 컬러, 폰트까지 Story에 맞게 설정되며 그중에서 가장 Simple한 안이 제시된다.
그속에 어떤 기술을 넣을지는 전적으로 개발자가 결정한다.
단지 UX전문가들이 제시하는 고객이 경험해야할 가치를 실제로 구현하는 것은 개발자의 몫이고 고객의 경험 가치를 기획할 뿐이다.

이속에는 Rich Client든, SOAP이든, Linux든, Java든 이런것은 없다.
기술을 부어줄 큰 틀을 UX전문가들이 만들고 나면 거기에 어떻게 구현할 것인지에 대한 질문만 남게 된다.
그럼 개발자들은 어떻게 구현할 것인가를 결정하면 되는것이다.

최근 아직도 논쟁되고 있는 Java와 C#의 성능 논쟁이나 Linux, Windows에 관련된 논쟁을 바라보며 아쉬운 생각이 들어서 이런 글을 포스팅한다.

왜 이런것이 짜증나는가?
Java는 Java가 아주 잘할 수 있는 영역이 있다.
IBM계열의 Cobol과 같은 기간계 시스템을 웹으로 올리는데 있어 Java 는 매우 탁월한 솔루션이며 기 구축된 Java based의 시스템에도 아주 잘 맞는 솔루션이다.
또한 Oracle과의 환상의 궁합도 잊으면 안되는 부분이다.
C#은 Windows서버에서의 궁합이 최상이며 다른 계열에 적용하는것은 아직은 생각할 수 없다.
DB도 당근 MSSQL계열과 최상이고 더 최상의 쓰임새는 Windows Application을 만드는 것 자체이다.
우리가 쓰고 있는 수많은 Windows Application 중에 많은 것이 .NET계열로 코딩된다.

이처럼 서로 사용하는 성능만으로 무엇인가를 이야기 하기 어려운 부분이 있다.
나름대로의 쓰임새를 보고 결정하면 될일을 기술이라는 틀만 가지고 무엇인가를 하려하는 습성이 한국 개발자들에게 개발팀에게 너무 많이 남아 있는 것 같아 씁쓸할 따름이다.
기술에 맞추다 보니 결국 고객의 경험 가치는 무시되기 때문에 더욱 더 짜증나는 것이다.

2010/02/22 09:17 2010/02/22 09:17