바이브 코딩을 하는 가장 큰 이유는 아마도 개발 생산성 향상 일 것이다. 그래서인지 바이브 코딩을 하면서 자연스럽게 "어떻게 하면 효율을 더 높일 수 있을까?"를 고민하게 된다. 직접 코드를 작성하지 않으니, 그만큼 남는 시간을 프로세스 개선에 투자하게 되는 셈이다. 지난 포스팅에서 앱과 서버를 함께 개발할 때 명세 문서를 작성하고, 이를 기반으로 AI 에이전트에게 개발을 지시하는 방식을 소개했었다. 오늘은 이 방식을 실제로 사용하면서 겪었던 불편함과, 그것을 어떻게 개선했는지 이야기해보려 한다. 기존 개발 환경 앱에 기능을 추가하다 보니 서비스가 하나가 아니라 여러 개가 되었다. 당시 내 개발 환경을 간단히 그려보면 이런 구조였다. 개발 Macbook └─ 앱 프로젝트/ ├─ docs/ └─ src/ Ubuntu 서버 ├─ server1 프로젝트/ │ ├─ docs/ │ └─ src/ └─ server2 프로젝트/ ├─ docs/ └─ src/ iOS 앱 개발은 어쩔 수 없이 Mac에서 해야 했다. 서버는 DB도 돌려야 하고, 실제 운영 환경과 동일한 조건에서 개발하는 게 나을 것 같아 Ubuntu에서 진행했다. Macbook 용량도 부족한데 DB 서버를 계속 띄워놓기도 부담스러웠고, 초기에 API 서버가 PHP + Apache 조합이었던 것도 분리한 이유 중 하나였다. 기존 개발 프로세스 새로운 기능을 추가하거나 수정할 때는 이런 순서로 작업했다. VS Code에서 앱 프로젝트를 열고, Claude를 이용해 명세를 작성 한다. 작성된 명세를 구현이 필요한 각 프로젝트의 docs 폴더에 복사 한다. VS Code에서 각 프로젝트를 연다. 서버의 경우 VS Code의 원격 연결을 이용한다. 각 프로젝트에서 Claude를 실행하고, docs 폴더의 명세를 기반으로 개발을 지시 한다. 문제점 문제...
Handshake error in client (OS Error: CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:393))
현재 flutter로 개발 중인 프로젝트에서 REST API 통신을 하는데, 안드로이드 단말에서 CERTIFICATE_VERIFY_FAILED 오류가 발생하면서 서버와 통신이 이뤄지지 않는 문제가 발생하였습니다.
현재 상황은 apache 서버에 php를 이용하여 api를 만들었고, 운영서버에는 SSL 이 적용되어 있습니다. SSL 인증서는 공인 인증 기관으로 부터 발급된 인증서를 사용하고 있습니다. iOS에서는 문제없이 서버와 통신이 이뤄지지만 안드로이드에서는 오류가 발생합니다.
CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate
"CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate"로 검색을 하면 공인 기관에서 발급하지 않고 자체적으로 인증서를 발급받아서 사용하는 경우에 문제 해결 방법들이 검색됩니다. 앱 내부에서만 사용하는 서버인 경우에 자체 발급된 인증서를 많이 쓰는 모양입니다.
자체 인증서로 인한 문제를 해결하는 방법은 두가지 정도가 있는 듯 합니다.
1. 인증서 검증을 하지 않는 방법
Dio dio = new Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(HttpClient client) {
client.badCertificateCallback = (X509Certificate cert, String host, int port) = true;
return client;
};
2. 인증서를 내장하는 방법
ByteData data = await rootBundle.load('assets/raw/certificate.pem');
SecurityContext context = SecurityContext.defaultContext;
context.setTrustedCertificatesBytes(data.buffer.asUint8List());
1번 방법은 저렇게 하면 굳이 돈을 지불하고 SSL을 지불하는 이유가 없고, 2번 방법은 인증서 변경될 때 마다 앱이 새로 배포되어야 하는 문제가 있습니다.
그리고, iOS에서는 문제가 없고, 공인된 기관에서 발급받는 인증서인데 자체 발급된 인증서와 같이 취급된다는것이 이해가 되지 않았습니다. 또한 동일 앱 내에서 외부 서버로 부터 파일을 받아오고 하는 것에는 문제가 발생하지 않고 있기 때문에 이 문제는 해결책이 아닌 것으로 보여서 좀 더 찾아보고 그래도 답이 없으면 1번 방법으로 진행하자라고 결론을 내렸습니다.
LetsEncrypt SSL 에서 발급받은 인증서의 경우에 Android 7 이상에서 유사한 문제가 발생한다는 내용이 있으나 LetsEncrypt SSL 통해서 발급된 인증서가 아니고 에러 메시지도 달라서 참고만 하는 걸로 결정합니다. (참고 : #)
그리고, System의 CA 저장소에 대한 접근이 되지 않는 경우에 동일한 문제가 발생할 수 있는 것으로 보입니다. 이것은 network-security-config 설정을 통해서 해결 할 수 있다고 합니다.
<network-security-config>
<base-config>
<trust-anchors>
<!-- Trust preinstalled CAs -->
<certificates src="system" />
<!-- Additionally trust user added CAs -->
<certificates src="user" />
</trust-anchors>
</base-config>
</network-security-config>
그러나 기본값은 허용이기 때문에 이것도 원인은 아닌듯 합니다. 이것이 허용되지 않았다면 당연히 다른 서버에서 파일 가져오는 것도 실패하는게 정상입니다.
SSLCertificateChainFile
답은 아파치 서버에 SSL 인증서를 적용하면서 인증서 체인 파일 지정(SSLCertificateChainFile)이 되지 않았을 때 발생하는 문제였습니다. 저의 경우에는 인증서 체인 파일을 복사 해놓았으나 지정하는 과정에서 오류가 있었습니다.
이렇게 지정되었을 때 일반 브라우저(안드로이드에서 동작하는 브라우저 포함)에서는 문제없이 접근이 가능합니다. 심지어 브라우저에서 인증서 정보를 조회하면 정상적으로 루트 인증서까지 정보 조회가 가능합니다. 그리고 위에서 말했듯이 iOS에서는 동일한 코드에서 문제가 발생하지 않습니다.
사소한 실수로 인해서 내가 발생시킨 오류이지만 flutter에서 에러 메시지라도 좀 다르게 해줬으면 문제해결이 빨랐지 않을까하는 생각이 듭니다.
댓글
댓글 쓰기