iOS 개발자랑 협업을 진행하면서 서버를 개발하는데, 프론트 팀에서 "간헐적으로 서버에 요청을 보내면 이런 에러가 뜨면서 연결이 실패해요."라고 아래 로그를 내게 보여줬다.
2024-01-21 16:31:12.778463+0900 fit-a-pet-client[4048:19194] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<SnapKit.LayoutConstraint:0x600001756280@FirstVC.swift#64 UIButton:0x135407450.width == 350.0>",
"<SnapKit.LayoutConstraint:0x6000017562e0@FirstVC.swift#65 UIButton:0x135407450.left == UIView:0x135408cc0.left + 16.0>",
"<SnapKit.LayoutConstraint:0x600001756340@FirstVC.swift#66 UIButton:0x135407450.right == UIView:0x135408cc0.right - 16.0>",
"<NSLayoutConstraint:0x6000010628f0 'UIView-Encapsulated-Layout-Width' UIView:0x135408cc0.width == 390 (active)>"
)
Will attempt to recover by breaking constraint
<SnapKit.LayoutConstraint:0x600001756280@FirstVC.swift#64 UIButton:0x135407450.width == 350.0>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2024-01-21 16:31:12.778819+0900 fit-a-pet-client[4048:19194] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<SnapKit.LayoutConstraint:0x6000017564c0@FirstVC.swift#86 UIButton:0x135407ea0.width == 350.0>",
"<SnapKit.LayoutConstraint:0x600001756640@FirstVC.swift#87 UIButton:0x135407ea0.left == UIView:0x135408cc0.left + 16.0>",
"<SnapKit.LayoutConstraint:0x6000017566a0@FirstVC.swift#88 UIButton:0x135407ea0.right == UIView:0x135408cc0.right - 16.0>",
"<NSLayoutConstraint:0x6000010628f0 'UIView-Encapsulated-Layout-Width' UIView:0x135408cc0.width == 390 (active)>"
)
Will attempt to recover by breaking constraint
<SnapKit.LayoutConstraint:0x6000017564c0@FirstVC.swift#86 UIButton:0x135407ea0.width == 350.0>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2024-01-21 16:31:15.003544+0900 fit-a-pet-client[4048:19194] AnonymousAlamofire - login() called userInput : heejin ,, heejin123
2024-01-21 16:31:15.193463+0900 fit-a-pet-client[4048:19646] [connection] nw_socket_handle_socket_event [C1.1.1.1:3] Socket SO_ERROR [54: Connection reset by peer]
2024-01-21 16:31:15.197219+0900 fit-a-pet-client[4048:19646] Connection 1: received failure notification
2024-01-21 16:31:15.197372+0900 fit-a-pet-client[4048:19646] Connection 1: received ECONNRESET with incomplete TLS handshake - generating errSSLClosedNoNotify
2024-01-21 16:31:15.197494+0900 fit-a-pet-client[4048:19646] Connection 1: failed to connect 3:-9816, reason -1
2024-01-21 16:31:15.197770+0900 fit-a-pet-client[4048:19646] Connection 1: encountered error(3:-9816)
2024-01-21 16:31:15.223664+0900 fit-a-pet-client[4048:19646] [connection] nw_socket_handle_socket_event [C2.1.1.1:3] Socket SO_ERROR [54: Connection reset by peer]
2024-01-21 16:31:15.224605+0900 fit-a-pet-client[4048:19646] Connection 2: received failure notification
2024-01-21 16:31:15.224681+0900 fit-a-pet-client[4048:19646] Connection 2: received ECONNRESET with incomplete TLS handshake - generating errSSLClosedNoNotify
2024-01-21 16:31:15.224732+0900 fit-a-pet-client[4048:19646] Connection 2: failed to connect 3:-9816, reason -1
2024-01-21 16:31:15.224885+0900 fit-a-pet-client[4048:19646] Connection 2: encountered error(3:-9816)
2024-01-21 16:31:15.243559+0900 fit-a-pet-client[4048:19646] [connection] nw_socket_handle_socket_event [C3.1.1.1:3] Socket SO_ERROR [54: Connection reset by peer]
2024-01-21 16:31:15.245081+0900 fit-a-pet-client[4048:19646] Connection 3: received failure notification
2024-01-21 16:31:15.245186+0900 fit-a-pet-client[4048:19646] Connection 3: received ECONNRESET with incomplete TLS handshake - generating errSSLClosedNoNotify
2024-01-21 16:31:15.245252+0900 fit-a-pet-client[4048:19646] Connection 3: failed to connect 3:-9816, reason -1
2024-01-21 16:31:15.245314+0900 fit-a-pet-client[4048:19646] Connection 3: encountered error(3:-9816)
2024-01-21 16:31:15.246229+0900 fit-a-pet-client[4048:19646] Task <95293D7D-6D91-4110-95FF-E208E3C0FE9D>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9816])
2024-01-21 16:31:15.252204+0900 fit-a-pet-client[4048:19641] Task <95293D7D-6D91-4110-95FF-E208E3C0FE9D>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey={숨김}, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <95293D7D-6D91-4110-95FF-E208E3C0FE9D>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <95293D7D-6D91-4110-95FF-E208E3C0FE9D>.<1>"
), NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey={숨김}, NSUnderlyingError=0x600003d20420 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816, _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: en0[802.11]}}, _kCFStreamErrorCodeKey=-9816}
Error: sessionTaskFailed(error: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey={숨김}, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <95293D7D-6D91-4110-95FF-E208E3C0FE9D>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <95293D7D-6D91-4110-95FF-E208E3C0FE9D>.<1>"
), NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey={숨김}, NSUnderlyingError=0x600003d20420 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816, _NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: en0[802.11]}}, _kCFStreamErrorCodeKey=-9816})
소켓 에러길래 그냥 단순히 에뮬레이터로 실행을 하면서 생기는 네트워크 에러인가 싶었는데, 실패하는 에러 문구를 보면 `TLS handshake` 과정에서 실패하고 있음을 알 수 있다.
- Connection reset by peer: Client 혹은 Server 측 둘 중 어딘가에서 강제로 연결을 끊을 때 발생하는 이슈
결론부터 이야기하자면, iOS랑 연동하면서 이런 에러가 난다면 서버 측의 문제가 맞다.
정상적인 서버라면 https 프로토콜을 적용을 했을 텐데, 해당 프로토콜은 TLS 핸드쉐이크 과정을 수반하여 인증키 교환을 한다.
그런데 iOS9 버전 이상부터는 보안 취약점을 막겠다고 모든 통신을 ATS(앱 통신 보안)으로 변경시켰고, 그로 인해 Https 연결을 TLS 1.2 이상으로 강제한다고 한다.
그런데 내 서버는 TLS 1.0부터 1.3까지 어미의 마음으로 모두 품어주고 있더라...
그래서 간헐적으로 1.1 이하 버전의 TLS 핸드 쉐이크가 걸리면 iOS 애뮬레이터에서 에러를 내뿜고 소켓 연결이 종료되는 것이었다.
문제 해결은 매우 간단하다.
나는 nginx를 썼기에 아파치 설정법은 잘 모르겠고, nginx는 server의 https 블럭에 다음을 추가해주면 된다.
server {
listen 443 ssl;
...
ssl_protocols TLSv1.2 TLSv1.3;
}
네트워크 검사 받아보니 확실히 TLS 1.2 이상만 사용하도록 강제하고 있다.
이러면 더 이상 iOS 연결에서 잘못된 버전의 TLS 핸드쉐이크가 발생하지 않는다.
🟡 여담
위 작업 끝내고 4시 30분에 잠들었다가 아침에 일어나보니 프론트팀에서 연락이 왔는데 camelCase 헤더가 죄다 소문자로 바뀌었다더라.
뭔가 했더니 겸사겸사 http 2.0 연결로 바꿨는데, 얘가 헤더 압축하고 디코딩하는 과정에서 사용자 정의 헤더를 죄다 소문자로 바꿔버림....🤦♂️
문제 하나 해결하고, 스스로 또 만들어버린 게 웃겨서 올려봤다.