반응형

ICT 분야의 스타트업을 팀빌딩할 때 빠질 수 없는 팀원이 개발자 팀원이다.

물론 기획자, 디자이너, 마케터 등 다른 파트의 팀원 또한 매우 중요한 역할을 담당하지만,

서비스를 개발하기에 앞서 초기에 비용이 가장 많이 투입되는 부분이 개발쪽이기에 부담이 큰 파트라는 부분은

어느정도 동의할 것이다.

 

여기서 내가 얘기하고자 하는 것은 단순한 개발자 팀원 뽑는 것보다 더 중요한 것은 CTO의 영입이라는 것이다.

심지어 개발 팀원을 두지 않고 외주 개발로만 진행한다하더라도 CTO를 영입하는 것을 권장한다.

단, 팀의 대표가 개발에 대한 역량이 없고, 개발에 대한 커뮤니케이션 능력이 없다는 가정이다.

 

여기서 CTO라는 것은 원론적인 의미의 CTO라기보다는 어느정도 중간 관리자 수준 급(개발자 컨트롤이 가능한)의 개발자를 의미하는 것으로 보면 될 것 같다.

초기 스타트업의 경우 당연히 각 분야 인재를 영입하는게 쉽지 않지만, 개발자 자체도 영입하는게 쉽지 않아서

그냥 개발자라면 무턱대고 뽑거나, 뽑는 것을 포기하고 바로 외주 업체를 찾아나선 후 개발을 시작하는 경우가 많다.

이것이 무조건 잘못됐다고 하는 것은 아니며 리스크가 있을 수 있는 부분을 설명하고자 한다.

 

이유는 여러가지가 있지만 여기서 살펴볼 이유는 '구현된 제품의 수준'이다.

어떤 고도화된 서비스 개발을 경험해보지 않은 개발자의 경우 아무리 초기 제품으로 MVP 모델을 만든다하더라도 이를 확장 모델로 고도화시키기가 매우 어렵게 만들곤 한다.

조금 더 쉽게 예를 들면, MVP 모델을 완성한 후 런칭을 하였는데 사용자가 급격히 늘어나며 데이타베이스에 데이타가 쌓이게 되면서 앱 속도가 점점 느려지고, 사용하기 어려울 정도가 되버리는 경우가 발생할 수 있다.(하나의 예이다.)

이 때 간단한 수정으로 해결될 수도 있지만 상황에 따라 설계의 문제라면, 다시 엎고 새로 만들어야 하는 경우까지 발생하게 된다.

 

나는 주변 스타트업에서 이런 상황을 직접 보기도 했고, 간접적으로 들은 것까지 포함하면 꽤나 흔한 상황이라 느꼈다.

이런 일이 발생하는 이유는 개발자가 이를 고려하지 않거나(혹은 역량 문제로 고려하지 못하거나),

외주에 의뢰하는 경우라면 이러한 디테일한 부분까지는 언급하지 않기 때문이다.

개발자의 역량이 높을 수록 당장의 구현에 급급하지 않고 앞으로를 고려한 설계를 신경쓰게 된다.

(귀차니즘의 문제로 그렇지 않은 경우도 있기는 하다.)

반대로 개발 역량이 낮을 수록 당장 구현에 급급할 뿐, 설계에 대한 노하우가 부족하므로 위와 같은 문제를

초래하게 된다. 

 

외주업체의 경우 당연히 외주 업체마다 case by case인 부분인데, 의뢰하는 경우 외주업체 입장에서는

단발성 프로젝트로 치부되는 경향이 있기 때문에, 고도화된 설계보다 프로젝트를 빨리 마무리하는데 중점을 두게 된다.

그래서 초기 스타트업이 외주 업체에 맡겨 개발한 초도 제품을 고도화시킬 때 쯤에는 새롭게 팀빌딩한 멤버들과 함께

처음부터 온전한 설계를 통해 새로 만드는 경우가 꽤 흔하다.

 

초기 스타트업은 개발 단가에도 민감할 수밖에 없는데, 무조건 싸게 진행하려는 것도 문제가 될 수 있다.

외주 업체는 특히 진행 비용에 맞추어 개발하는 경향이 있기 때문에 개발 단가는 제품 퀄리티에 영향을 크게 미친다.

만약 CTO에 버금가는 인력이 있었다면 단가 문제를 떠나서 외주를 주던, 개발자 팀원을 뽑던 위와 같이 발생할 수 있는 문제를 최소한으로 줄일 수 있었을 것이다.

 

물론 개발하려는 제품에 따라, 혹은 개발하는 개발자에 따라 위와 같은 문제가 없을 수 있고 의외로 순조로울 수도 있다.

하지만 많은 스타트업에서 이런 문제가 발생하고, 또 스타트업이 아니더라도 일반 기업에서 위시캣, 프리모아 등과 같은 개발 중개 사이트를 통해 진행하는 경우에도 개발에 대한 트러블이 꽤나 많이 발생하므로 위 내용을 어느정도 주의하는 것은 좋을 것 같다.

반응형
반응형

 

여기서는 STM32CubeMX 5.6.1 버전을 기준으로 할 것이다.

(어짜피 버전이 달라도 UI 또는 편의성 정도의 차이일 것이기에 크게 문제될 것은 없을 것 같다.)

 

MCU는 STM32103C8Tx(LQFP 48) 기준으로 작업할 것이다.

이것 또한 MCU가 다르다고 크게 다를 것은 없다.

 

여기서 MCU의 10번 핀을 PWM 핀으로 사용해볼 것이다.

데이타시트에 보면 확인할 수 있듯이 LQFP48에 대한 10번은 TIM2_CH1 기능을 사용할 수 있도록 되어있다.

타이머 채널들은 모두 PWM이 사용 가능하다고 보면 된다.

이제 STM32CubeMX를 실행하여 MCU의 기본적인 세팅을 해준다.(크리스탈, 디버거 설정 등)

나의 경우 8MHZ 크리스탈이 달려있고 ST-LINK를 사용할 것이기에 RCC_OSC 설정을 해주었고,

PA13, PA14쪽의 SWCLK와 SWDIO 설정도 같이 해주었다.

 

이제 10번 핀(PA0)을 PWM 핀으로 설정해보도록 하겠다 해당 핀을 클릭하면 아래와 같은 메뉴가 뜬다.

여기서 TIM2_CH1을 클릭한다.

이와 같이 노란색으로 바뀌었으며 이 상태는 핀을 사용하겠다고는 했지만 디테일한 세팅이 되어있지 않은 것이다.

위와 같이 TIM2에서 Channel1을 선택한 후 PWM Generation CH1을 선택해준다.

 

이와 같이 설정이 완료된 상태로 변경되었다.

이제 가장 중요한 설정을 할 차례다.

원하는 PWM 주파수를 만들기 위해 Prescaler와 주기 값 등을 지정해주어야 한다.

아래 파라미터 설정을 보고 설명하도록 하겠다.

 

가장 중요한 세가지 값이 있다.

Prescaler와 Counter Period로 PWM 주기를 지정하고 Pulse 값으로 Duty를 지정할 수 있다.

먼저 주기를 설정하기 위해서는 나의 타이머가 얼마의 클럭으로 돌아가는가를 알아야 한다.

 

STM32CubeMX의 Clock Configuration 탭을 확인하면 다음과 같이 클럭을 확인할 수 있다.

위와 같이 APB1 Timer clocks과 APB2 timer clocks를 확인하면 된다.

자신이 어떤 타이머를 사용할지에 따라 위, 아래의 클럭 중 어떤 것을 사용하는지가 결정된다.

이 또한 아래의 데이타시트 Clock tree에서 확인할 수 있다.

이는 MCU마다 차이가 있을 수 있으므로 반드시 Datasheet를 확인해야 한다.

TIM1은 APB2를 쓰고 TIM2,3,4는 APB1을 쓴다. 

나는 여기서 TIM2_CH1을 쓸 것이니 APB1 클럭만 신경쓰면 된다.

 

200hz의 50% Duty의 PWM 파형을 만들어보도록 하겠다.

 

Prescaler = 799

Counter Period = 49

PWM Pulse = 99 

이렇게 지정하였다.

여기서 각 값들이 800, 50, 100도 아니고 1씩 떨어져있는 것을 볼 수 있는데,

이것은 각 값들이 0부터 시작하므로 실제 적용할 값에서 -1을 해주어야 한다.

이제 위 값들이 어떻게 계산되는지 알아보도록 하겠다.

 

APB Clock 이 8Mhz였으므로, 먼저 해당 클럭이 프리스케일링된다.

8,000,000 / 800 (Prescaler) = 10,000 Hz(10Khz)

 

여기서 Counter Period에 의해 200Hz로 조정된다.

10,000 Hz / 50 (Counter Priod) = 200Hz

 

이제 200Hz 주기인 PWM의 Duty를 50% 로 지정한다.

200 / 100 (Pulse) = 2 (50% Duty)

 

CubeMX 에서의 모든 설정은 마쳤다고 볼 수 있다.

여기서 모두 값을 지정해주었지만, 얼마든지 코드 상에서 값을 수정할 수도 있다.

이제 우측 상단의 GENERATE CODE를 눌러서 코드를 생성하고 해당 코드를 불러오도록 한다.

PWM 파형을 동작시키는 코드를 작성해보겠다.

 

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); 한줄만 추가해주면 되므로 매우 간단하다. 

코드를 보면 유추할 수 있듯이 TIM2의 CH1의 PWM을 동작하도록 하는 코드이다.

이제 컴파일 후에 실행하여 결과를 확인해보면 정상적으로 PWM 파형이 발생하고 있을 것이다.

(참고로 Unknown Target 과 같은 컴파일 오류가 발생한다면 프로젝트 옵션에서 Target MCU 설정을 해주어야 한다.)

반응형
반응형

[2021-08-20T07:53:00,865][INFO ][org.logstash.beats.BeatsHandler][main][85cdcfd8ac49789653328fffd54627260e267cb5c24953396322a6d4da0521f2] [local: 172.11.0.3:5000, remote: 142.128.153.77:53930] Handling exception: io.netty.handler.codec.DecoderException: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69 (caused by: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69)
[2021-08-20T07:53:00,865][WARN ][io.netty.channel.DefaultChannelPipeline][main][85cdcfd8ac49789653328fffd54627260e267cb5c24953396322a6d4da0521f2] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:477) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:404) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:371) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:354) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.AbstractChannelHandlerContext.access$300(AbstractChannelHandlerContext.java:61) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.AbstractChannelHandlerContext$4.run(AbstractChannelHandlerContext.java:253) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:66) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.65.Final.jar:4.1.65.Final]
        at java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69
        at org.logstash.beats.Protocol.version(Protocol.java:22) ~[logstash-input-beats-6.1.6.jar:?]
        at org.logstash.beats.BeatsParser.decode(BeatsParser.java:62) ~[logstash-input-beats-6.1.6.jar:?]
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:507) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:446) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        ... 11 more
------------------------------------------------------------------------------------------------------------------------------

 

현재 로깅 시스템 구축을 위해 FileBeat로 LogStash에 로깅 데이타를 Shipping 하는 방법으로 구성해놓았다.

그런데 로그스태시쪽에서 위와 같은 오류를 뿜어내기 시작했다. 

org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69 이런 오류와 동시에

org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 71 오류가 발생하기도 한다.

이런 경우 오류 내용에서도 알 수 있듯이 버전 호환이 잘 되지 않는 것이다.

위 문제 발생시 내가 쓰고 있는 ELK 스택의 버전은 7.14.0이었고, FileBeat의 버전은 7.4.2였는데 버전 차이로 인한

프로토콜의 차이가 발생하여 위와 같은 에러가 발생하는 것으로 추측했다.

FileBeat 버전도 최신버전인 7.14.0으로 업그레이드하고 테스트해보니 해당 문제는 사라졌다.

 

반응형

+ Recent posts