还有人不知道&和&&的区别?
掘金
&和&&是什么意思?这其实是一个非常简单的问题,问题虽然简单,但是还是很容易忽略它的用法,这里只是总结一下两者的区别和实际用途。
Score: 57.1Confidence: 54%
View offerLoading the catalog…
THE AI OPPORTUNITY INDEX
Find your next AI tool. Explore free access, trials, and credits — all in one place.
掘金
&和&&是什么意思?这其实是一个非常简单的问题,问题虽然简单,但是还是很容易忽略它的用法,这里只是总结一下两者的区别和实际用途。
Score: 57.1Confidence: 54%
View offer人人都是产品经理
旭日图看占比方便,想看季度和月度的绝对数值与趋势就没那么顺手。作者把有层级的数据做成套娃图,外层是季度、内层是月度,一眼就能找到最大值和最小值,还能给达标与不达标的月份填不同颜色。 前面 旭日图 的文章中我分享过下面这样的可视化图表,这个图表对于有层级结构的数据看占比时特别方便,但如果想看绝对数值和趋势对比,可能就没那么方便了。 比如下面4个季度12个月里面,哪个季度营业额最高/最低,哪个月最高/最低,我还得切换到百分比模式(会比看数值更快),然后再分别从内环和外环里面去挑那个最大值/最小值。 有没有更好的方式呢?有的。这种图表没有专业的叫法(或者有,但我不知道),我个人将其叫做套娃图。 1 什么是套娃图 套娃图顾名思义,就是有点像俄罗斯套娃的那种感觉。具体到上图的示例,就是外层展示季度数据,内层展示月度数据,把月度数据嵌套在季度数据里面。 这样想看季度看外层,想看月度看内层数据,而且比旭日图更直观,能一眼看到季度/月度最大值/最小值,如下图所示。 上图和文章开头的数值完全一致,读者可以拿2张图分别试下,明显这种套娃图找数据会更快一些,而且还能看出月度或季度的趋势。 这点是旭日图所不具备的。而且上面的套娃图我们还可以将业绩完成率达标的月份和不达标的月份填充不同颜色,无论点击月度还是季度柱状图的时候,都会出现底部的达成率卡片说明。 套娃图整体使用了3种填充背景色,而旭日图至少是4种(月度和季度有颜色深浅区别),整体看上去更清爽了。 2 套娃图的演变和拓展 这个套娃图横轴即X轴是月份数据,纵轴即Y轴是营业额数据,显示12条数据既能保证图表美观又能保证数字清晰,那如果有更多的数据项呢? 我试着做了下横版的效果,整体的效果如下图所示,整体感觉没有上面的显示效果好看精致。 当然,上面的这个图也可以是非等距的,例如3个大区下面分别管辖3家、4家和5家门店,最终的图片就会特别别扭,如下图所示。 这么看来,套娃图还真的是天生就为月度/季度数据准备的。 3 套娃图和旭日图怎么选 工具没有对错,只看适用与否。 如果你是想看月度/季度这种12条以内具有层级结构的数据,且是等距的,那么套娃图是不二选择。 如果你的数据非等距且数量超过12条,或者有三层结构,或者你更注重占比而不是绝对数值,那么旭日图或许不错。 如果你实在拿不定主意,你可以试着通过2种方式做出Demo,然后看哪种可视化形式更好看更易用或更易理解,那么就选哪种。 本文由人人都是产品经理作者【詹师兄】,微信公众号:【詹师兄】,原创/授权 发布于人人都是产品经理,未经许可,禁止转载 题图来自 Unsplash,基于 CC0 协议
Score: 57.02Confidence: 54%
View offerInfoQ - 促进软件开发领域知识与创新的传播
点击查看原文>
Score: 56.2Confidence: 54%
View offervelog
안녕하세요. 현재 개인프로젝트로, TransferTracker 라는 축구 이적 정보 서비스를 개발하고 있습니다. 선수 이적 정보 / 선수 검색 / 팀 검색 / 팀 정보 / 기자별 이적 소식 / 팀 이적정보 등등을 한곳에서 확인할 수 있도록 하는 서비스이고 현재 백엔드와 프론트 모두 기본 기능은 구현된 상태입니다. 현재 서비스에서는 다음과 같은 기능들을 확인할 수 있습니다. https://transfertracker-front.vercel.app/ 선수 및 이적 검색 선수별 이적 이력 조회 5대 리그 클럽 조회 클럽별 소속 선수 조회 기자별 / 클럽별 이적 게시물 조회 해외 이적 게시물 -> 한국어로 번역 선수 사진 / 팀 엠블럼 / 5대리그 팀 한글 팀명 적용 사용중인 툴 들은 다음과 같습니다. Java / Spring Boot / Spring Boot JPA / MySQL Sepring Security ( 간단하게 ) OpenAI API / API-Football API / X API Docker 추가 예정입니다. 해당 프로젝트를 조금 더 완성도 있게 완성하고싶어서 같이 진행할 팀원분을 모십니다. Frontend - 1명. 저 또한 실력이 낮기때문에 부담없이 진행하셔도 괜찮습니다. 즉, 경험이 많지 않아도 / 프로젝트를 진행한 경험이 별로없거나 없어도 괜찮습니다. 대신, 꾸준하게 참여하고 맡은 부분에는 책임감있게 진행할 수 있는 분이면 좋겠습니다. 참여를 원하시는 분은 아래 Google Form 을 작성해주세요. https://forms.gle/Hi3kgDXf74XouXYm9
Score: 54.37Confidence: 49%
View offervelog
인터프리터는 명령을 실행할 때마다 다음에 무엇을 처리할지 선택합니다. 덧셈처럼 명령 자체가 단순하면 이 디스패치 비용도 설계에서 따져 볼 문제가 됩니다. Jimmy Ostler는 자신의 ternary 프로젝트를 개선하려는 탐색 중 Scala의 VM 디스패치 비교에 착안해 Rust 구현을 실험했으며, 그 과정을 2026년 8월 1일 「Tail-Call Interpreters in Rust」 에 공개했습니다. 이 실험을 읽을 때 중심에 둘 질문은 다음 명령으로 넘어가는 흐름을 Rust에서 어떻게 표현하고 보장하느냐입니다. 꼬리 호출은 돌아올 일을 남기지 않습니다 꼬리 호출 최적화는 함수의 마지막 호출을 점프로 처리해 호출 프레임이 계속 쌓이지 않도록 합니다. 다른 함수를 호출한 뒤 그 결과를 그대로 반환한다면, 호출한 쪽으로 돌아와 추가로 수행할 작업이 없습니다. 따라서 코드가 재귀 형태라는 이유만으로 실행 중 호출 스택도 계속 늘어난다고 판단해서는 안 됩니다. Ostler의 설명에 따르면 Rust는 높은 최적화 수준에서 이런 변환을 수행할 수 있습니다. 실험에 사용한 explicit_tail_calls 는 컴파일러의 선택에 맡기던 변환을 명시적으로 요구하는 불안정 기능입니다. 예제의 become 은 해당 호출을 꼬리 호출로 처리하거나 오류를 내도록 요구합니다. 안정판 Rust에서 그대로 사용할 수 있는 구문으로 받아들여서는 안 됩니다. 여기서 재사용하는 호출 프레임과 계산 값을 보관하는 값 스택은 별개입니다. 명령 전환 때 호출 프레임이 누적되지 않아도, 실행하는 프로그램이 값을 계속 넣으면 값 스택의 공간은 소진됩니다. 꼬리 호출이 보장하는 범위는 호출 흐름이며, 값 저장 공간의 관리는 머신 설계에 남습니다. 명령 실행은 상태를 넘기는 과정입니다 예제의 dispatch 는 명령 하나를 처리한 뒤 갱신한 스택 포인터와 명령 포인터를 다음 호출에 전달합니다. 명령 집합은 Lit , Add , Sub , Mul , Div 로 구성됩니다. Lit 는 값을 스택에 추가하고, 나머지는 산술 연산을 수행합니다. 산술 명령 안에서 값이 이동하는 순서는 다음과 같습니다. 실행 상태를 담는 sp 는 값 스택의 위치를, ip 는 실행할 명령의 위치를 나타냅니다. 값을 추가한 분기는 sp + 1 과 ip + 1 을 전달합니다. 산술 분기는 두 피연산자가 결과 하나로 바뀌므로 sp - 1 과 ip + 1 을 전달합니다. 각 분기 끝의 become dispatch(...) 에 이 상태 변화가 드러납니다. 종료 조건은 명령 포인터가 명령 배열의 길이에 도달하는 것입니다. 이때는 다음 명령으로 이동하지 않고 값 스택의 마지막 값을 반환합니다. 코드를 읽을 때는 각 연산의 계산식뿐 아니라 분기가 넘기는 포인터와 종료 시 사용하는 위치를 함께 보면 실행 상태를 따라갈 수 있습니다. 꼬리 호출 뒤에도 명령 선택은 남습니다 예제는 자기 자신을 꼬리 호출하면서도 하나의 match 에서 명령 종류를 고르는 switch dispatch 구조를 유지합니다. 일반적인 switch dispatch 설명에는 반복문이 등장하지만, Ostler가 제시한 비교 기준 코드는 재귀 호출로 실행을 이어 갑니다. 명령을 어디서 선택하는지와 다음 실행을 어떤 구문으로 표현하는지는 서로 다른 설계 요소입니다. become 을 사용했다는 사실이 중앙의 명령 판별까지 제거하지는 않습니다. 글에서 이어서 소개하는 subroutine dispatch는 명령 선택 구조를 바꾸려는 시도입니다. 이 비교에서 꼬리 호출 여부만 보면 명령을 선택하는 방식의 차이를 놓칩니다. 실제 처리 시간에는 명령 선택 외에도 상태 접근과 산술 연산이 함께 반영되므로, 디스패치 구조를 비교할 때는 실행에 포함되는 작업을 함께 봐야 합니다. 실험 코드에는 입력 전제가 있습니다 이 예제를 제품 코드로 옮기려면 전역 상태의 소유권과 명령열의 유효성을 별도로 설계해야 합니다. 예제는 크기 32의 값 스택과 전역 명령 배열을 사용하며, 구현에는 static mut 와 unsafe 가 들어갑니다. Ostler는 Scala 구현과 형태를 맞추기 위한 선택이었다고 설명하면서 이런 Rust 작성 방식을 권장하지 않는다고 밝힙니다. 비교 대상과 코드 형태를 맞추려는 목적이 상태 관리 방식에 반영된 것입니다. 실제 구현을 위한 설계 제안으로는 VM 인스턴스가 값 스택과 명령 배열을 소유하도록 구성하는 방향이 있습니다. 소유 구조를 바꾼 뒤에도 명시적 꼬리 호출을 적용할 수 있는지는 다시 점검해야 합니다. 배열 접근에는 필요한 값이 이미 존재한다는 전제도 있습니다. 산술 명령은 sp - 2 와 sp - 1 을 읽고, 실행이 끝나면 sp - 1 을 반환합니다. 따라서 빈 프로그램, 피연산자가 부족한 명령열, 값 스택의 용량을 초과하는 입력을 어떻게 처리할지 정해야 합니다. 이 입력 조건은 재귀 호출을 어떤 방식으로 실행하는지와 독립된 책임입니다. 성능 비교에는 같은 실행 기준이 필요합니다 속도를 비교하려면 동일한 명령열에서 동일한 계산 결과를 얻는 구현을 대상으로 측정해야 합니다. 소개된 subroutine dispatch 설명은 구현 도중에 끝나며, 완성된 구현과 벤치마크 수치가 함께 제시되지 않아 두 방식의 속도 우열은 정해져 있지 않습니다. 따라서 코드의 형태를 보고 어느 쪽이 더 빠른지 결론 내리기보다, 비교할 실행 조건부터 맞추는 것이 이 글의 제안입니다. 직접 비교하는 경우에는 컴파일러와 최적화 설정도 기록하는 편이 좋습니다. Rust가 최적화 수준에 따라 꼬리 호출 변환을 수행할 수 있다는 설명을 고려하면, 어떤 설정에서 실행했는지는 결과를 해석하는 데 필요한 조건입니다. 계산 결과가 같은지 살피는 일도 명령 전환 구조를 바꾸면서 머신의 동작을 유지했는지 판단하는 기준이 됩니다. 또한 이 실험의 대상은 단순한 스택 머신입니다. Ostler는 더 복잡한 레지스터 머신을 구분해 다루겠다고 밝혔습니다. 스택 머신에서 얻은 측정 결과를 다른 구조에 적용하려면 그 구조에서의 검증이 별도로 필요합니다. 어떤 경우에 검토할 만한가 꼬리 호출 방식은 다음 명령으로 넘길 상태와 제어 흐름을 실험하려는 인터프리터 구현에 검토할 만합니다. 단순한 머신을 통해 명령 실행과 다음 호출의 연결을 이해하려는 경우에도 구체적인 출발점이 됩니다. 반면 비교 실험의 코드를 곧바로 제품 구현의 틀로 선택하는 것은 맞지 않습니다. 작성자가 설명한 코드 형태의 목적을 고려하면, 제품 적용에서는 디스패치 구문을 고르는 일과 실행 상태를 관리하는 일을 함께 설계하는 편이 타당합니다. 마무리 이 실험을 적용할 때는 다음 실행에 필요한 상태와 그 소유자를 먼저 정하고, 컴파일러에 요구할 호출 특성을 명확히 하는 것이 우선입니다. 호출 스택의 누적을 막는 설계를 마련한 뒤, 속도 개선 여부는 실제로 실행할 명령열에서 측정해 판단해야 합니다. 원문: webi 기술 블로그 참고한 자료: Tail-Call Interpreters in Rust – Jimmy Ostler Jeeves - 판단 전에 추론하는 Jev 방식의 의사결정 모델 Claude 일부 서비스 장애
velog
무슨 일이 있었나 미국 법무부(DOJ)가 2026년 10월 1일, 캘리포니아주 시티오브인더스트리 소재 기술기업 '어스메이드 컴퓨터(Earthmade Computer Inc.)'의 대표 그렉 루이(Greg Lui)를 수출통제법 위반 등 3개 혐의로 기소하고 체포했다고 발표했다. 혐의 내용은 엔비디아 AI 칩이 탑재된 수출통제 대상 고성능 서버를 미 상무부 허가 없이 3억 달러 이상 규모로 중국에 밀반출했다는 것이다. 검찰에 따르면 루이와 공모자들은 2023~2024년 사이, 최종 사용지를 허위로 기재해 서버를 말레이시아·싱가포르로 먼저 수출했다. 이 두 나라는 해당 품목에 별도 수출 허가가 필요 없는 경유지였다. 이후 이 서버들은 다시 중국으로 불법 재수출됐으며, 어스메이드는 2024년 1월부터 10월 사이 말레이시아 소재 선적업체 두 곳으로부터 1억 7,600만 달러 이상을 수령한 것으로 조사됐다. 루이에게 적용된 혐의는 수출통제개혁법(ECRA) 및 수출관리규정(EAR) 위반 공모, 대외 밀수, 자금세탁 공모 등 3가지로, 공모죄는 각각 최고 20년, 밀수죄는 최고 10년의 징역형에 처해질 수 있다. 왜 중요한가 이 사건은 미국의 대중 AI 반도체 수출통제가 현실에서 어떻게 우회되고 있는지를 보여주는 구체적 사례다. 말레이시아·싱가포르처럼 수출 허가가 면제되는 제3국을 경유해 통제 품목을 세탁하는 '우회 수출' 패턴은 이미 여러 사건에서 반복적으로 드러난 수법인데, 이번 건은 단일 사건 규모가 3억 달러에 달해 역대 최대급으로 꼽힌다. 업계 관점에서 이 사건은 두 가지 함의를 갖는다. 첫째, 엔비디아의 최신 AI 가속기에 대한 중국 내 '그레이마켓' 수요가 여전히 막대하며, 공식 규제로는 이를 완전히 틀어막지 못하고 있다는 점이다. 둘째, 미 정부가 경유국을 통한 재수출 네트워크 적발에 수사력을 집중하고 있다는 신호로, 향후 동남아 경유 거래에 대한 모니터링과 제재가 한층 강화될 가능성이 높다. 이는 엔비디아를 포함한 반도체 기업들에게 유통망 실사(end-use verification) 부담을 가중시키는 동시에, AI 칩 공급망 전반에 걸친 지정학적 리스크가 당분간 해소되기 어렵다는 점을 재확인시킨다. 원문: https://www.justice.gov/opa/pr/california-man-arrested-smuggling-more-300-million-export-controlled-computer-servers-china
Score: 54.35Confidence: 49%
View offervelog
무슨 일이 있었나 앤스로픽이 2026년 10월 2일, 기업용 AI를 실제로 구축·배포할 수 있는 엔지니어를 양성하는 교육 프로그램 '클로드 프론티어 아카데미(Claude Frontier Academy)'를 공개했다. 2027년 말까지 1억 달러를 투입해 '프론티어 배포 엔지니어(Frontier Deployed Engineer, FDE)' 1만 명을 길러내는 것이 목표다. 프로그램은 의대 레지던트 과정을 본뜬 구조로 운영된다. 참가자는 앤스로픽 엔지니어들과 함께하는 다일간의 대면 부트캠프를 거치고, 실제 기업 배포 상황을 재현한 시뮬레이션 과제를 수행한 뒤, 자신이 소속된 조직에서 12주간 현장 레지던시를 밟는다. 수료 단계는 '클로드 레지던트 엔지니어' 배지에서 '클로드 프론티어 배포 엔지니어' 배지로 올라간다. 첫 코호트는 샌프란시스코, 뉴욕, 런던에서 이미 시작됐으며 액센츄어, 베인, 캡제미니, 커먼웰스뱅크오브오스트레일리아, 딜로이트, 맥킨지, 모건스탠리, 노보노디스크 등 대형 컨설팅·금융·제약사 소속 엔지니어들이 참여하고 있다. 첫 수료생은 2027년 초에 나올 예정이다. 왜 중요한가 이번 발표는 '모델 성능'이 아니라 '도입 역량'이 AI 확산의 진짜 병목이라는 업계의 인식 변화를 보여준다. 앤스로픽 글로벌 사업개발 총괄 스티브 코필드는 "적절한 역량을 갖춘 소수의 고역량 인력이 회사 전체를 바꿀 수 있다"고 밝혔는데, 이는 많은 기업이 AI 도입 의지는 있지만 이를 실제 프로덕션 환경에 통합할 숙련된 인력이 부족하다는 문제의식에서 나온 것이다. 특히 참여 기업 명단에 컨설팅 빅4와 글로벌 투자은행, 제약사가 포함된 점은 주목할 만하다. 앤스로픽이 단순히 API 판매에 그치지 않고, 대형 엔터프라이즈 고객사의 내부 역량 자체를 자사 생태계 안으로 끌어들이는 '락인(lock-in)' 전략을 구사하고 있다는 해석이 가능하다. 오픈AI 역시 엔터프라이즈 영업과 교육에 공을 들이고 있는 가운데, 이번 투자는 두 회사의 경쟁이 모델 스펙 경쟁에서 '배포 생태계' 경쟁으로 옮겨가고 있음을 시사한다. AI 인재 공급 부족이 기업의 AI 도입 속도를 좌우하는 핵심 변수로 떠오른 만큼, 유사한 사내 인증·교육 프로그램이 다른 빅테크에서도 잇따를 가능성이 크다. 원문: https://www.anthropic.com/news/claude-frontier-academy
Score: 54.35Confidence: 49%
View offervelog
무슨 일이 있었나 독일 AI 이미지 생성 스타트업 블랙포레스트랩스(Black Forest Labs)가 신형 이미지 생성 모델 'FLUX 3 Image'를 공개했다. 이번 모델의 핵심은 '다른 픽셀은 건드리지 않는' 정밀 편집 기능이다. 사용자가 0~1000 좌표 캔버스 위에 바운딩 박스로 각 객체의 위치와 설명(id)을 지정하면, 모델은 지정한 영역만 수정하고 나머지 이미지는 그대로 유지한 채 멀티턴(다단계) 편집을 수행한다. 이 외에도 FLUX 3 Image는 최대 10장의 레퍼런스 이미지를 조합해 하나의 장면을 구성할 수 있고, 네이티브 2K·4K 해상도 출력을 지원한다. 텍스트-이미지 변환, 이미지-이미지 변환, 이미지 내 텍스트 렌더링, 포토리얼리즘 등 기존 FLUX 시리즈의 강점도 그대로 이어받았다. 현재는 기업 고객을 대상으로 자체 배포·파인튜닝용 모델 가중치 구매가 가능하며, 누구나 접근할 수 있는 오픈 가중치 버전은 수주 내 공개될 예정이다. 왜 중요한가 이번 발표에서 가장 눈에 띄는 지점은 '좌표 기반 바운딩 박스'를 통한 편집 제어 방식이다. 기존 이미지 생성 모델들은 자연어 프롬프트만으로 편집을 지시해야 해서 의도치 않은 영역까지 바뀌는 문제가 잦았는데, FLUX 3 Image는 이를 구조화된 좌표값으로 지정해 재현성과 예측 가능성을 크게 높였다. 이는 광고·커머스·게임 에셋 제작처럼 '정확히 이 부분만' 바꿔야 하는 실무 워크플로우에서 실질적인 생산성 향상으로 이어질 수 있다. 또한 10장까지 레퍼런스 이미지를 조합할 수 있다는 점은 캐릭터 일관성이나 브랜드 톤을 유지해야 하는 콘텐츠 제작사에 특히 매력적인 기능이다. 블랙포레스트랩스는 이미지 생성 분야에서 미드저니·오픈AI·구글 등과 경쟁하고 있는데, 기업용 가중치 선판매 후 오픈 가중치 공개라는 순서를 택한 점은 수익화와 오픈소스 생태계 확장을 동시에 노리는 전형적인 '이중 전략'으로 읽힌다. 생성 AI 이미지 시장이 범용 품질 경쟁에서 '정밀 제어' 경쟁으로 넘어가고 있다는 흐름을 보여주는 사례다. 원문: https://bfl.ai/models/flux-3-image
Score: 54.35Confidence: 49%
View offervelog
이 글에서 다룰 주제 AIOps와 이상 탐지 : 평소와 다른 상태를 찾는 일은 장애 판단과 어떻게 다른가? Isolation Forest의 원리 : 무작위로 나누는데 어떻게 이상을 찾을까? 경로 길이와 점수 : 논문의 점수와 Python API는 왜 방향이 반대일까? 실습과 검증 : 시간순으로 데이터를 나누고 임계값을 정하는 방법 운영 적용 전 점검 : 계절성, 오탐, 데이터 누수, 드리프트 주요 단어 · AIOps · 비지도 학습 · 피처 · Isolation Tree · 경로 길이 · contamination · 임계값 · 데이터 누수 CPU 사용률이 80%를 넘으면 언제나 장애일까? 배치 작업이 예정된 시간이라면 정상일 수 있다. 반대로 CPU는 40%인데 지연시간과 오류율이 함께 높아지는 상황은 살펴볼 필요가 있다. AIOps에 이상 탐지를 도입하기 전에, 모델이 무엇을 보고 어떤 근거로 점수를 내는지부터 이해하려고 한다. 첫 번째 학습 대상으로 Isolation Forest 를 정리한다. 알고리즘의 직관에서 출발해 직접 실행한 합성 데이터 실습, 운영 적용 전에 확인할 조건까지 이어간다. 1. AIOps: 이상 탐지는 어느 단계에 있을까? AIOps (Artificial Intelligence for IT Operations) — 운영 데이터를 분석해 이상 징후 탐색, 사건의 연관 관계 파악, 원인 조사와 대응을 지원하는 접근이다. 이 글에서 생각하는 작업 흐름은 다음과 같다. 메트릭 수집 → 시간 구간별 피처 생성 → 이상 점수 계산 → 알림 정책 적용 → 로그·트레이스·변경 이력 조사 → 대응 판단 Isolation Forest는 주로 이상 점수 계산 을 맡는다. “평소의 데이터와 얼마나 다른가?”에 대한 단서를 주지만, “어느 코드가 원인인가?”나 “서비스를 재시작해야 하는가?”까지 답하지는 않는다. 이상(anomaly) 은 학습 데이터의 패턴에서 벗어나는 상태다. 장애(incident) 는 사용자나 서비스에 실제로 문제가 발생한 사건이다. 둘은 일치할 수도 있지만 같은 뜻은 아니다. 새 기능 출시로 정상 트래픽이 증가해도 이상으로 잡힐 수 있다. 반대로 학습 기간 내내 성능이 나빴다면 그 상태를 흔한 패턴으로 볼 수도 있다. 그래서 이상 점수에 서비스 영향과 운영 맥락을 함께 붙여야 한다. 관측 데이터의 차이가 낯설다면 메트릭·로그·트레이스·프로파일 정리 부터 읽으면 연결하기 쉽다. 2. 입력 데이터: 모델이 보는 것은 피처 벡터다 피처(feature) — 모델에 입력하는 개별 측정값이나 가공한 값이다. 윈도우(window) 는 값을 집계하는 시간 구간이다. 예를 들어 서비스 하나를 1분 단위로 관찰한다면 한 행을 다음처럼 만들 수 있다. 아래 값은 설명용이다. 시각 CPU 평균 (%) p95 지연시간 (ms) 오류율 (%) 요청 수 (건/분) 10:00 42 128 0.2 900 10:01 44 132 0.2 930 10:02 43 310 1.4 920 마지막 행의 CPU만 보면 앞선 두 행과 비슷하지만 지연시간과 오류율은 다르다. 여러 피처를 함께 넣으면 이런 조합을 검토할 수 있다. 단, 피처를 여러 개 넣었다는 이유만으로 모든 상관 관계 이상을 잘 탐지한다고 보장되지는 않는다. p95 는 요청 지연시간의 95번째 백분위수다. 95%의 요청이 대략 그 값 이내에 끝났다는 뜻이다. 서로 다른 인스턴스의 p95를 단순 평균한 값을 전체 서비스의 p95로 취급하면 안 된다. 방식 주로 묻는 질문 해석할 때 볼 점 고정 임계값 지연시간이 500ms를 넘었나? 명확한 서비스 기준을 표현하기 좋음 변화·계절성 기준 같은 시간대의 평소 수준과 다른가? 기준 기간과 계절 패턴이 필요 Isolation Forest 입력된 피처 공간에서 쉽게 고립되는 상태인가? 학습 데이터와 피처 설계에 따라 의미가 달라짐 고정 임계값과 모델 점수는 함께 사용할 수 있다. 사용자 영향이 큰 오류율 경보를 모델 도입만으로 없앨 이유는 없다. 3. Isolation Forest: 드문 점은 적은 분할로 고립된다 Isolation(고립) — 데이터를 반복해서 나누어 특정 관측값을 다른 관측값에서 분리하는 것이다. Isolation Tree 는 이 분할 과정을 담은 트리이고, Isolation Forest 는 여러 트리를 모은 모델이다. 한 트리를 만드는 핵심 과정은 단순하다. 학습 데이터에서 일부 행을 뽑는다. 현재 노드에서 피처 하나를 무작위로 고른다. 그 피처의 최솟값과 최댓값 사이에서 분할 기준을 무작위로 고른다. 기준보다 작은 쪽과 큰 쪽으로 나누고 반복한다. 관측값 하나만 남거나, 더 나눌 수 없거나, 깊이 제한에 도달하면 멈춘다. 그림 1. 직접 구성한 좌표와 분할 예시. 실제 학습 결과의 트리 경로가 아니라 고립의 의미를 설명하는 그림이다. 그림 왼쪽에서는 CPU < 75 라는 한 번의 질문으로 주황색 A를 다른 점에서 분리한다. 오른쪽의 B는 다음 순서를 거친다. 분할 B가 따라가는 가지 그 가지에 남는 점 수 1 CPU < 75 예 10 2 지연시간 < 150 예 5 3 CPU < 32 아니오 2 4 지연시간 < 135 예 1 경로 길이(path length) — 루트에서 해당 점이 도착하는 말단 노드까지 지나간 간선 수다. 위 예시에서 A는 1, B는 4다. 분할이 무작위이므로 어떤 트리에서는 평범한 점도 우연히 빨리 분리될 수 있다. 따라서 한 트리로 판단하지 않고 여러 트리의 경로 길이를 평균낸다. 이 방식의 직관은 수가 적고 다른 점들과 값이 다른 관측값은 평균적으로 더 빨리 고립되기 쉽다 는 것이다. 원 논문 여기서 “멀리 떨어짐”은 그림을 이해하기 위한 표현이다. 알고리즘이 점 사이의 거리를 직접 계산하거나 정답 라벨로 좋은 분할을 찾는 것은 아니다. 4. 이상 점수: 경로 길이를 비교 가능한 값으로 바꾸기 표본이 32개인 트리와 256개인 트리의 경로 길이는 그대로 비교하기 어렵다. 점이 많으면 분리하는 데 필요한 질문 수도 달라질 수 있기 때문이다. 논문은 평균 경로 길이를 기준값으로 정규화해 다음 점수를 정의한다. 여기서는 트리 하나에 사용한 표본 수를 $\psi$로 쓴다. $$ s(x,\psi)=2^{-\frac{E[h(x)]}{c(\psi)}} $$ $x$: 점수를 계산할 관측값 $h(x)$: 한 트리에서의 경로 길이 $E[h(x)]$: 여러 트리에서 얻은 경로 길이의 평균 $c(\psi)$: 표본 수에 따른 경로 길이의 정규화 기준 깊이 제한에 걸려 말단에 여러 점이 남아 있다면, 현재 깊이에 그 점들을 더 분리하는 데 필요한 평균 길이의 추정값을 더한다. 따라서 실제 계산은 무조건 말단 깊이만 세는 방식이 아니다. 정규화 기준은 다음과 같다. $$ c(\psi)=2H_{\psi-1}-\frac{2(\psi-1)}{\psi} $$ $H_m=1+\frac12+\cdots+\frac1m$은 조화수다. 구현에서는 로그를 이용한 근사를 사용할 수 있다. $c(1)=0$, $c(2)=1$로 처리하며, 여기서는 표본이 충분한 일반적인 경우를 생각한다. 논문의 점수 정의와 알고리즘 그림 2. 함수 $s=2^{-u}$를 직접 계산한 그래프. $u$는 정규화한 평균 경로 길이다. 평균 경로가 기준의 절반이면 점수는 약 0.707, 기준과 같으면 0.5, 기준의 1.5배이면 약 0.354다. 짧은 경로 → 큰 점수 → 더 이상한 후보 로 읽으면 된다. 0.707은 “장애 확률 70.7%”가 아니다. 이 점수에는 장애 여부에 대한 확률 보정이 없다. 0.5 역시 모든 서비스에 그대로 적용할 운영 경보 기준은 아니다. 5. scikit-learn: 점수 방향과 contamination을 구분하기 Python으로 옮기면 점수의 방향부터 주의해야 한다. 값 더 이상한 방향 용도 논문의 $s(x,\psi)$ 클수록 고립 정도를 점수로 표현 score_samples(X) 작을수록 원 점수의 반대 방향 -score_samples(X) 클수록 이 글에서 사용할 이상 점수 decision_function(X) 작을수록 기본 임계값을 반영한 판단값 predict(X) -1 은 이상, +1 은 정상 기본 임계값으로 이진 판정 관계는 decision_function = score_samples - offset_ 이다. decision_function < 0 이면 기본 판정에서 이상이다. 공식 API 문서 contamination 은 학습 데이터의 점수를 나눌 기준에 관여한다. 0.01 이면 학습 점수의 낮은 쪽 약 1%를 이상으로 구분하도록 경계를 정한다. 같은 점수가 여럿이면 비율이 정확히 맞지 않을 수 있다. contamination=0.01 은 다음을 뜻하지 않는다. 실제 장애 비율이 1%라는 사실을 모델이 알아냈다. 미래 데이터에서도 언제나 1%만 이상으로 표시된다. 이상한 행을 자동으로 삭제한 뒤 학습한다. "auto" 는 실제 이상 비율을 추정하는 옵션이 아니라 기본 점수 기준을 사용한다. 이때 offset_=-0.5 다. 다른 조건과 난수 시드가 같다면 contamination 변경은 트리 자체보다 판정 경계 를 바꾼다. 이번 실습에서도 원점수의 동일성을 확인했다. 주요 설정은 n_estimators (트리 수), max_samples (트리당 행 수), max_features (트리당 사용할 피처 수), random_state (재현용 난수 시드)다. max_samples="auto" 는 min(256, 학습 행 수) 를 사용한다. 이 값들이 모든 환경에서 최적이라는 뜻은 아니다. 설정 설명 6. 실습: 과거 데이터로 학습하고 미래 구간에서 확인하기 6.1 데이터와 검증 설계 아래는 실제 운영 데이터가 아닌 합성 데이터 다. 한 행은 1분이며, 총 1,440분 동안 CPU, p95 지연시간, 오류율을 만든다. 정상 값에는 240분 주기와 잡음을 넣고, 테스트 구간에만 30분 동안 지연시간과 오류율을 높인다. 구간 인덱스 역할 학습 0~719 트리 학습 보정 720~999 정상으로 구성한 별도 구간에서 임계값 결정 테스트 1000~1439 모델과 임계값을 고정한 후 최종 평가 테스트 정답은 평가할 때만 사용한다. 정답을 보며 임계값을 고르면 실제보다 좋은 결과가 나올 수 있다. 이번 임계값은 보정 점수의 99번째 백분위수 다. 정상 보정 데이터의 높은 꼬리를 기준으로 삼는 간단한 실습 정책이며, 운영 권장값은 아니다. 미래 오탐률 1%를 보장하지도 않는다. 6.2 실행 코드 실행 환경은 Python 3.9.6, NumPy 2.0.2, scikit-learn 1.6.1, SciPy 1.13.1이다. 고정 시드를 사용했으며 환경에 따라 마지막 소수 자릿수는 달라질 수 있다. import numpy as np from sklearn.ensemble import IsolationForest from sklearn.metrics import precision_score, recall_score rng = np.random.default_rng(42) t = np.arange(1440) load = 0.5 + 0.3 * np.sin(2 * np.pi * t / 240) cpu = 25 + 35 * load + rng.normal(0, 2, len(t)) latency = 80 + 90 * load + rng.normal(0, 5, len(t)) error = np.clip( 0.15 + 0.15 * load + rng.normal(0, 0.03, len(t)), 0, None, ) X = np.column_stack([cpu, latency, error]) y = np.zeros(len(t), dtype=int) # 테스트 구간에만 인위적 이상 주입: 끝 인덱스 1150은 제외 X[1120:1150, 1] += 140 # 지연시간 +140ms X[1120:1150, 2] += 1.0 # 오류율 +1%p y[1120:1150] = 1 X_train = X[:720] X_cal = X[720:1000] X_test = X[1000:] y_test = y[1000:] model = IsolationForest( n_estimators=200, max_samples=256, contamination="auto", random_state=42, ) model.fit(X_train) # 큰 값 = 더 이상함. 보정 구간으로 임계값을 결정한다. cal_score = -model.score_samples(X_cal) threshold = np.quantile(cal_score, 0.99) test_score = -model.score_samples(X_test) is_anomaly = test_score > threshold tp = np.sum(is_anomaly & (y_test == 1)) fp = np.sum(is_anomaly & (y_test == 0)) fn = np.sum(~is_anomaly & (y_test == 1)) tn = np.sum(~is_anomaly & (y_test == 0)) print(f"threshold={threshold:.4f}") print(f"TP={tp}, FP={fp}, FN={fn}, TN={tn}") print(f"precision={precision_score(y_test, is_anomaly):.4f}") print(f"recall={recall_score(y_test, is_anomaly):.4f}") 직접 실행한 출력은 다음과 같다. threshold=0.5994 TP=30, FP=4, FN=0, TN=406 precision=0.8824 recall=1.0000 여기서는 별도로 정한 임계값을 사용하므로 model.predict() 의 결과와 혼용하지 않는다. contamination="auto" 의 기본 경계와 위의 0.5994는 서로 다른 판정 정책이다. 그림 3. 위 실습의 지연시간과 테스트 이상 점수. 모델 입력은 CPU·지연시간·오류율 세 가지이고, 위쪽 패널은 그중 지연시간만 보여 준다. 6.3 결과에서 읽을 것과 읽으면 안 되는 것 정밀도(precision) 는 이상으로 표시한 것 중 실제 이상인 비율이다. 이번에는 34개 중 30개이므로 약 88.2%다. 재현율(recall) 은 실제 이상 중 탐지한 비율이며, 30개 중 30개라 100%다. 정상인 410개 중 4개가 오탐이므로 테스트의 점 단위 오탐률은 약 0.98%다. 이것은 이 실행의 결과이며 이후에도 유지된다는 보장이 없다. 30분 연속된 한 사건을 30개의 정답으로 평가했다는 점도 중요하다. 30개를 맞힌 것이 서로 다른 장애 30건을 찾았다는 뜻은 아니다. 운영에서는 사건을 놓쳤는지, 최초 탐지까지 얼마나 걸렸는지, 중복 알림이 얼마나 발생했는지도 평가해야 한다. 이 예제의 이상은 정상 범위에서 크게 벗어나도록 만들었고 학습·보정 구간에는 이상을 넣지 않았다. 따라서 높은 재현율은 모델의 일반적인 성능 증거가 아니다. 실제 데이터에서는 작은 변화, 정상 배포, 결측, 복수 장애와 드리프트를 별도로 확인해야 한다. 7. 운영 적용: 모델보다 먼저 정해야 할 것 7.1 시간의 의미를 피처에 담기 Isolation Forest에 행을 넣는다고 시간 순서나 주기를 자동으로 이해하는 것은 아니다. 이번 예제에서도 모델에 시간 t 를 입력하지 않았다. 학습 구간에 반복적으로 나타난 값의 조합을 보고 점수를 계산했을 뿐이다. 시간대에 따라 정상 범위가 달라진다면 최근 값의 차이, 과거 이동평균 대비 편차, 같은 요일·시간대 기준 대비 잔차 등을 검토할 수 있다. 예를 들어 시각 $t$의 값을 직전 5분 평균 과 비교하려면 pandas에서는 series.shift(1).rolling(5).mean() 처럼 현재 값 이전의 구간을 명시한다. 미래를 포함한 중앙 이동평균이나 미래 데이터로 계산한 기준은 실시간 탐지 입력으로 사용할 수 없다. 데이터 누수(data leakage) — 예측 시점에 알 수 없는 정보가 학습·전처리·임계값 결정 과정에 들어가는 문제다. 학습·검증 분리는 시간순으로 하고, 피처 계산에 사용하는 과거 데이터의 범위를 정한다. 겹치는 윈도우나 예측 대상의 기간 때문에 경계가 새어 들어가면 적절한 간격도 둔다. 전처리 기준값은 학습 구간으로 정하고 이후 구간에 고정 적용한다. 7.2 서비스 차이와 데이터 품질 확인하기 확인 항목 실무에서 필요한 질문 비교 집단 성격이 다른 API·배치·DB를 한 모델에 섞고 있지 않은가? 집계 구간 CPU, 오류율, 요청 수가 같은 서비스와 시간 범위를 나타내는가? 결측 수집 실패를 정상적인 0으로 바꾸고 있지 않은가? 오류율 요청이 0건일 때의 정의와 최소 요청 수 조건이 있는가? 변경 이력 배포·스케일 변경·정기 작업 때문에 패턴이 바뀌었는가? 전처리 학습과 추론에서 컬럼 순서·단위·변환이 같은가? 누적 요청 카운터는 그대로 넣기보다 목적에 맞는 구간 증가량이나 rate로 바꾸고 리셋을 처리한다. 전처리 없이 메트릭 이름만 골라 넣는 방식은 모델보다 데이터 문제에 더 크게 영향을 받을 수 있다. 표준화는 거리 기반 모델처럼 항상 필수인 것은 아니다. 축별 선형 스케일 변경은 이상적인 무작위 분할에서 상대적인 분할 위치를 보존한다. 반면 log1p 같은 비선형 변환은 분할 분포를 바꾸므로 별도 선택이며, 효과를 검증해야 한다. 7.3 점수를 알림 정책과 분리하기 점 하나가 임계값을 넘었다고 곧바로 담당자에게 알릴 필요는 없다. 연속 초과 조건, 일정 윈도우에서의 초과 횟수, 사건별 묶기, 재알림 간격 등을 둘 수 있다. 예를 들어 “최근 5분 중 3분 초과”는 설명용 정책이지 모든 서비스의 정답이 아니다. 짧지만 심각한 장애를 놓치거나 탐지를 늦출 수 있으므로 알림 정책 적용 후의 성능도 측정한다. SLO(Service Level Objective) 는 지연시간이나 성공률 같은 서비스 품질의 목표다. 이상 점수와 함께 SLO 영향, 오류율, 사용자 영향을 보면 조사 우선순위를 정하는 데 도움이 된다. 정상 패턴에서 드문 상태라는 사실만으로 심각도를 결정하지 않는다. 7.4 드리프트와 재학습 관리하기 드리프트(drift) — 시간이 지나면서 입력 데이터나 정상 패턴의 분포가 바뀌는 현상이다. 새 배포 이후 정상 패턴이 달라지면 기존 모델의 오탐이 늘 수 있다. 그렇다고 최근 데이터를 자동으로 모두 학습시키면 진행 중인 장애를 정상 패턴에 포함할 위험도 있다. 학습 기간, 피처 정의, 모델 버전, 임계값 버전을 함께 남기고 재학습 후보를 별도 검증한다. 새 모델은 처음부터 대응을 자동화하기보다 점수와 알림 후보를 기록하는 방식으로 관찰하고 기존 경보와 비교하는 편이 좋다. 표준 Isolation Forest는 축에 평행한 분할을 사용한다. 기울어진 상관 구조나 복잡한 정상 집단에서 기대와 다른 점수를 낼 수 있고, 무관한 피처를 많이 추가하면 유효한 신호가 약해질 수 있다. 특정 데이터에서 효과가 있는지는 규칙 기반 기준선과 비교해서 확인해야 한다. 8. 학습 정리: 도입 전 답해 볼 질문 이 알고리즘을 설명할 때 기억할 연결은 무작위 분할 → 고립까지의 경로 길이 → 여러 트리의 평균 → 점수 → 운영 임계값 이다. 도입 전에 아래 질문에 답할 수 있으면 모델과 운영 정책의 경계가 명확해진다. 한 행은 어떤 서비스의 어느 시간 구간을 나타내는가? 학습 기간은 정상 운영의 여러 상태를 충분히 포함하는가? 높은 점수가 장애 확률이 아닌 이유를 설명할 수 있는가? 임계값은 어떤 별도 데이터와 운영 목표로 정했는가? 점 단위 정밀도 외에 장애 단위 탐지율·탐지 지연·일일 알림 수를 측정하는가? 이상 후보가 생겼을 때 어떤 로그·트레이스·변경 이력을 조사할 것인가? 다음 학습에서는 원시 메트릭을 어떤 피처로 바꿀지, 계절성과 알림 기준을 어떻게 다룰지를 더 구체적으로 살펴볼 수 있다. 참고 자료와 재현 기준 Liu, Ting, Zhou, Isolation Forest, ICDM 2008 — 고립과 평균 경로 길이에 기반한 원 논문. PDF scikit-learn, IsolationForest API — 점수 방향, offset, 파라미터 scikit-learn, Novelty and Outlier Detection — 학습 데이터 안의 이상 탐지와 새로운 관측값 탐지의 구분 글의 좌표 예시·합성 데이터·그래프는 설명을 위해 직접 구성하고 계산했다. 운영 환경을 측정한 결과가 아니다. 문서는 2026-10-03에 확인했으며
velog
task.cancel() 은 깃발만 꽂는 것 let task = Task { await doSomething() } task.cancel() task.cancel() 을 호출해도 doSomething() 이 그 즉시 멈추지 않는다. Swift의 Task 취소는 협조적 취소(cooperative cancellation) 모델을 따르기 때문이다. cancel() 은 그 Task의 isCancelled 플래그를 true 로 바꿀 뿐, 실행 중인 코드를 강제로 중단시키지 않는다. 실제로 멈추는 동작은 그 코드 내부에서 "지금 취소됐나?"를 직접 확인하고 반응하도록 개발자가 직접 구현 해야 한다. 구조화된 작업 vs 비구조화된 작업 — 취소가 전파되는 범위가 다르다 Swift Concurrency는 크게 두 가지로 나뉜다. 구조화된 작업(Structured Concurrency) — async let , TaskGroup . 작업이 선언된 스코프 안에서만 살아있고, 스코프를 벗어나면 자동으로 정리된다. 비구조화된 작업(Unstructured Concurrency) — Task { } , Task.detached { } . 스코프와 수명이 묶여있지 않고 독립적으로 존재한다. 취소 전파는 구조화된 작업에서만 잘 일어난다. 이걸 확인하기 위해, 취소 여부를 직접 체크하면서 1초씩 5번 도는 함수를 하나 만들어보자. func work(prefix: String) async { for i in 0..<5 { if Task.isCancelled { print(prefix, "cancelled") return } try? await Task.sleep(for: .seconds(1)) print(prefix, i) } } 이 함수를 구조화된 작업( async let )과 비구조화된 작업( Task { } ) 양쪽에서 동시에 실행시키고, 바깥에서 전체를 취소해보자. func execute() async { Task { await work(prefix: "🔴 nested task") // 비구조화 } async let _ = work(prefix: "🟡 async let") // 구조화 await work(prefix: "🟣 task") // 구조화 — 같은 Task 안에서 직접 await } let task = Task { await execute() } task.cancel() 🟡 async let cancelled 🟣 task cancelled 🔴 nested task 0 🔴 nested task 1 🔴 nested task 2 🔴 nested task 3 🔴 nested task 4 task.cancel() 을 호출하자마자 🟡 async let 과 🟣 task 는 바로 취소됐지만, 🔴 nested task 는 취소 신호를 전혀 못 받고 5번을 끝까지 다 돈다. 🟣 task 는 별도의 Task 경계를 만들지 않고 execute() 를 감싸는 Task 안에서 그대로 실행되기 때문에, 그 Task가 취소되면 isCancelled 도 바로 같이 true 가 된다. 부모 Task( execute() 를 감싸는 Task)를 취소해도, 내부에 새로 만든 Task { } 는 별도의 작업으로 취급돼서 취소가 전파되지 않기 때문 이다. Task 가 생성 시점의 context(우선순위 등)는 상속하지만, 취소 상태까지 상속하는 건 아니다. 중첩된 Task { } 를 취소하고 싶다면 그 Task를 따로 들고 있다가 직접 cancel() 을 호출해야 한다. 반면 async let 은 구조화된 작업이라서, 부모가 취소되면 그 취소가 자동으로 전파된다. TaskGroup 도 마찬가지다. func execute() async { await withTaskGroup(of: Void.self) { group in group.addTask { await work(prefix: "🔵 group task 1") } group.addTask { await work(prefix: "🔵🔵 group task 2") } } } let task = Task { await execute() } task.cancel() 🔵 group task 1 cancelled 🔵🔵 group task 2 cancelled group.addTask { } 로 추가한 자식 작업들도 TaskGroup 안에 구조화돼있기 때문에, 바깥 Task가 취소되면 그 취소가 그대로 전파돼서 두 작업 모두 즉시 취소된다. async let 의 특이한 동작 — 즉시 시작하지만, 결과를 안 쓰면 취소된다 async let 은 선언되는 즉시(= await 를 기다리지 않고) 자식 Task로 등록되어 백그라운드에서 실행된다. func execute() async { async let result = work(prefix: "async let") await someOtherWork() // 이 await가 스레드에 틈을 만들어줘야 async let도 실제로 돌 기회를 얻는다 print(await result) } 여기서 async let 이 "즉시 시작된다"는 건 Task로 등록 된다는 뜻이지, 그 순간 바로 스레드 위에서 실행 된다는 보장은 아니다. 실제로 코드가 돌려면 스케줄러가 틈을 낼 기회(다른 suspend 지점)가 있어야 한다. 그리고 더 중요한 규칙 하나 — async let 으로 만든 결과값을 한 번도 await 하지 않은 채로 그 스코프를 벗어나면, Swift는 그 작업을 암묵적으로 취소시킨다. func execute() async { async let _ = work(prefix: "async let") print("end") // 바로 다음 줄에서 스코프가 끝남 } 이 코드는 work 가 단 한 번도 제대로 실행되지 못하고 취소된다. async let 은 "자기를 만든 스코프보다 더 오래 살아남으면 안 된다"는 구조화된 작업의 규칙을 따르기 때문에, 결과를 안 쓰고 스코프가 끝나버리면 Swift가 정리 차원에서 취소시켜버리는 것이다. 전파된 취소를 실제로 이용하는 두 가지 방법 isCancelled 플래그가 켜졌다는 걸 알았다고 해도, 그걸 코드에서 확인하는 방법은 성격이 다른 두 가지로 나뉜다. 폴링(polling) 방식 — Task.checkCancellation() / Task.isCancelled 코드 중간중간에서 "지금 취소됐나?"를 직접 확인해야 반응할 수 있다. 위에서 본 work(prefix:) 의 if Task.isCancelled { ... } 가 바로 이 방식이다. Task.checkCancellation() 은 같은 역할을 하되, Bool 을 리턴하는 대신 취소 상태면 CancellationError 를 던진다(throw). 반복문이나 시간이 걸리는 작업 중간중간에 넣어서, 비싼 작업을 하기 전에 취소 여부를 확인하는 용도로 쓴다. 이벤트 기반(event-driven) 방식 — withTaskCancellationHandler 폴링과 다르게, 취소되는 "순간" 자동으로 콜백이 호출된다. await withTaskCancellationHandler { await work(prefix: "handler") } onCancel: { print("cancelled immediately") } onCancel 은 operation 이 실행되는 동안 Task가 취소되는 그 즉시 호출된다. 중간중간 직접 체크하지 않아도 되기 때문에, completion handler 기반의 레거시 API처럼 취소라는 개념 자체를 모르는 코드를 Swift Concurrency의 취소 체계에 끼워 넣을 때 특히 유용하다. 정리 task.cancel() 은 즉시 멈추는 명령이 아니라, isCancelled 플래그를 세우는 신호일 뿐이다. 그 신호가 전파되는 범위는 구조화된 작업( async let , TaskGroup )인지 비구조화된 작업( Task { } , Task.detached { } )인지에 따라 다르다. 비구조화된 작업은 취소를 상속하지 않는다. async let 은 즉시 등록되어 백그라운드에서 돌지만, 결과를 한 번도 await 하지 않고 스코프를 벗어나면 암묵적으로 취소된다. 전파된 취소 신호를 코드에서 실제로 이용하려면, 중간중간 직접 확인하는 폴링 방식( checkCancellation / isCancelled )과 취소되는 순간 자동으로 반응하는 이벤트 기반 방식( withTaskCancellationHandler ) 중 상황에 맞는 걸 골라 써야 한다.
velog
Our Premium Lawyers platform is built around legal education and academic information. We provide students with useful explanations and resources covering a variety of legal topics, helping them approach their studies and research with greatser clarity.
Score: 54.33Confidence: 49%
View offervelog
코디로그 — 로그인 전에는 데이터 자체를 보내지 않는 미션 체크 도구 화면에서 안 보이게 가리는 것과, 서버가 애초에 안 보내는 것은 다릅니다. 기준: 2026-10 프로젝트 개요 코디로그는 코디세이 AI 올인원 과정의 미션 13개(이후 Term Project 2개 추가로 15개)를 체크리스트 형태로 관리하는 개인 웹 도구입니다. 공식 플랫폼이 해주지 않는 딱 한 가지, "지금 내가 어디까지 했는지 한눈에 보기"만 잘하는 도구로 범위를 좁혀서 만들었습니다. 저장소가 비공개라 이 글에서 기능을 README보다 조금 더 자세히 적습니다. 링크 배포: https://cody-log-iota.vercel.app 저장소: 비공개 (미션 원문이 포함돼 있어 공개하지 않습니다) 설계 철학 — "로그인 안 해도 되는 건, 정말 로그인 안 해도 되는 선까지만" 처음엔 로그인 없이도 전체 기능을 쓸 수 있게 만들었는데, 이후 보안서약 문제로 구조를 바꿨습니다. 미션 체크리스트 항목 텍스트는 코디세이 원문을 요약·재구성한 것이라, 보안서약상 로그인 없이 노출되면 안 됩니다. 그래서 지금은 디스코드 로그인(코디세이 서버 소속 확인)을 거쳐야 미션 내용을 볼 수 있습니다. 대신 기초개념 공부 페이지(코디세이 원문이 아니라 제가 직접 정리한 내용)는 로그인 없이 그대로 열어뒀습니다. 지금까지 한 것 (2026-10 기준) 미션 체크리스트 : 15개 미션의 기능요구사항·제약사항·제출물을 세부 항목 단위로 체크. 다 체크하면 상위 항목 자동 체크. 전체 진행률은 미션 개수 기준으로 동일 비중 계산(항목 수가 많은 미션이 과하게 반영되지 않도록) 필터·정렬 : 필수/선택, 개인/팀, 미션 그룹, 즐겨찾기 기준 필터링. 카드 드래그로 순서 변경, 한 줄에 보이는 카드 개수 조절 기초개념 공부 : 미션별 핵심 개념 사전, 플래시카드, 퀴즈 모드. 퀴즈 결과는 로그인 시 기록으로 남아 마이페이지에서 조회 가능 사이렌 기간 · 장학금 계산 : 기수별 D-day 표시, 동료평가·출입시간 입력 시 월별 장학금 기준 충족 여부 자동 계산 GitHub 연동 · 메모 · MD 내보내기 : 미션별 저장소 링크(공개 API로 커밋 정보 조회) 등록, 자유 메모, 체크리스트를 마크다운으로 내보내 AI 피드백에 활용 모바일 전용 화면 + PWA : 반응형 대신 별도 레이아웃, 홈 화면에 앱처럼 설치 가능 디스코드 로그인 : 코디세이 디스코드 서버 소속 여부를 OAuth 스코프로 확인, 서버 멤버만 미션 내용 열람 트러블슈팅 CSS로 가렸는데 원문이 그대로 보인다는 피드백 화면에서 안 보인다고, 데이터가 안 보내진 건 아니었습니다. 상황: 퍼실리테이터분이 "블러 처리는 잘 보이는데, 개발자 도구로 보면 미션 원문이 그대로 보인다"고 알려주셨습니다. 원인: 미션 원문 페이지를 빌드 시점에 미리 만들어두는 방식(정적 생성)을 쓰고 있었는데, 빌드할 때는 로그인 여부를 알 수 없어서 "로딩 중" 상태로 간주되고, 제가 짠 차단 조건( 로딩이 끝났는데 미인증이면 막기 )을 그냥 지나쳐서 원문이 그대로 정적 HTML에 구워졌습니다. 브라우저에서 열면 그 다음에 다시 로그인 여부를 확인해 화면만 가렸을 뿐, 서버가 처음 내려준 HTML 자체엔 원문이 이미 들어있었습니다. 해결: 해당 페이지를 "요청이 올 때마다 서버가 그 자리에서 만드는" 방식으로 바꾸고, 로그인 세션을 쿠키 기반으로 전환( @supabase/ssr )해서 서버가 매 요청마다 로그인 상태를 먼저 확인하게 했습니다. 미인증이면 원문 데이터 자체를 컴포넌트에 넘기지 않아서, 서버가 만드는 HTML에 원문이 아예 안 들어갑니다. View Page Source로 직접 확인해서 검증했습니다. 메인 체크리스트 화면도 같은 문제였습니다 원문 페이지 하나만 고치고 끝난 게 아니라, 같은 구조의 문제가 다른 화면에도 있었습니다. 상황: 원문 페이지를 고친 뒤, 메인 미션 체크 화면(카드 그리드)도 같은 방식으로 점검했습니다. 원인: 체크박스를 실시간으로 누르고 드래그로 순서를 바꾸는 화면이라, 미션 데이터 전체를 클라이언트 컴포넌트에서 직접 불러오고 있었습니다. 로그인 여부와 상관없이 체크리스트 텍스트가 브라우저로 내려가는 JS 파일 자체에 평문으로 포함돼 있었습니다. CSS 블러는 그 데이터를 화면에서 가릴 뿐, 개발자 도구의 Sources 탭에서 번들 파일을 열면 그대로 보이는 구조였습니다. 해결: 로그인 상태에 따라 데이터를 다르게 구성해서 내려주는 서버 컴포넌트를 두고, 체크리스트를 그리는 쪽은 그 결과를 props로만 받게 바꿨습니다. 미인증 사용자에게는 체크리스트 항목 배열 자체를 빈 값으로 보내서, 빈 공간 대신 자리만 보여주는 더미 줄을 그리도록 했습니다. 검증하다가 제가 만든 버그를 하나 더 찾았습니다 빌드 결과물을 직접 열어서 확인하는 과정 자체가 버그를 하나 더 잡아줬습니다. 상황: 위 작업을 하면서 "로그인 없이도 봐도 되는 필드(제목, 카테고리 등)"만 따로 뽑은 파일을 만들었는데, 처음엔 정규식으로 텍스트를 추출했습니다. 원인: 정규식이 미션 블록 경계를 정확히 못 잡아서, 일부 미션의 제약사항 텍스트가 "로그인 없이 공개해도 되는 데이터"에 섞여 들어갔습니다. 빌드된 JS 파일을 직접 열어서 "보호해야 할 문장이 실제로 번들에 있는지" 하나하나 대조하다가 발견했습니다. 해결: 정규식 대신 원본 데이터 파일을 실제로 파싱해서(타입 선언부만 제거하고 require 로 읽는 방식) 공개용 파일을 다시 만들고, 미션 15개 전체·보호 대상 문장 약 460개를 원본과 하나하나 대조하는 검증 스크립트를 돌렸습니다. OAuth 콜백에서 겪은 두 가지 문제 브라우저 전용 코드가 서버에서 실행되는 문제, 그리고 쿼리스트링이 리다이렉트 허용 목록 매칭을 깨뜨리는 문제. 상황 1: 로그인 후 같은 페이지로 돌아오게 하려고 서버 쪽 유틸 파일을 만들었는데, 로그인은 되지만 인증 확인이 들쭉날쭉했습니다. 원인 1: 서버 전용 파일이 설정값 하나를 가져오려고 클라이언트 전용 모듈을 그대로 불러오고 있었고, 그 모듈 최상단에서 브라우저 전용 함수가 모듈을 불러오는 순간 바로 실행되고 있었습니다. 브라우저 환경이 없는 서버에서 이 함수가 실행되면서 동작이 불안정해졌습니다. 해결 1: 환경변수 값만 담은 순수 모듈을 따로 분리해서, 서버 쪽 코드가 클라이언트 전용 모듈을 아예 거치지 않게 했습니다. 상황 2: 로컬에서 로그인하면 분명 로컬 주소에서 시작했는데, 승인하고 나면 배포 주소로 돌아갔습니다. 원인 2: "로그인 후 원래 페이지로 돌아가기" 기능을 넣으면서 리다이렉트 주소에 쿼리스트링을 붙였는데, Supabase의 리다이렉트 허용 목록은 쿼리스트링까지 정확히 같아야 통과시켜주는 방식이었습니다. 등록해둔 주소와 실제 요청 주소가 쿼리스트링 때문에 달라서 매칭에 실패했고, 매칭 실패 시의 기본 주소(배포 주소)로 대신 보내지고 있었습니다. 해결 2: 허용 목록에 와일드카드( ** )를 추가해서 쿼리스트링이 붙어도 매칭되게 했습니다. 퀴즈에서 선택지가 매번 바뀌어 보인다는 제보 정답을 고르는 순간마다, 화면 뒤에서 문제 자체가 다시 계산되고 있었습니다. 상황: 사용자분이 "정답을 고른 뒤 선택지가 다시 섞이고, 이전 선택지 일부가 안 사라지고 남아있다"고 알려주셨습니다. 원인: 오답 3개를 뽑는 함수를 리렌더될 때마다 다시 호출하고 있었습니다. 정답을 고르면 상태가 바뀌면서 리렌더가 일어나는데, 그 순간 오답이 또 랜덤으로 다시 뽑히면서 화면에 보이는 선택지 자체가 바뀌었습니다. 해결: 문제 번호가 바뀔 때만 선택지를 다시 계산하도록 캐싱( useMemo )했습니다. 겸사겸사 데스크톱·모바일이 각자 따로 갖고 있던 퀴즈 코드를 하나로 합쳐서, 같은 버그가 한쪽에만 남는 일이 없게 했습니다. 다음 계획 당장 추가 기능 계획은 없습니다. 심화 과정 자료가 나오면 그때 미션 데이터를 추가할 예정입니다. 정리 CSS로 가리는 것과 서버가 안 보내는 것은 다르다는 걸, 실제로 데이터가 새는 걸 보고 나서야 체감했습니다. 피드백 하나를 고치고 끝내지 않고, 같은 구조의 문제가 다른 화면에도 있는지 점검하는 과정에서 제가 만든 버그를 하나 더 찾았습니다. 빌드 결과물을 직접 열어서 "실제로 안 보내지는지" 대조하는 과정 자체가, 눈으로 보는 테스트보다 더 믿을 만한 검증이었습니다.
Score: 54.36Confidence: 49%
Score: 54.35Confidence: 49%
Score: 54.34Confidence: 49%
Score: 54.33Confidence: 49%