diff --git a/.obsidian/plugins/copilot/data.json b/.obsidian/plugins/copilot/data.json index 721c8d3..19e2080 100644 --- a/.obsidian/plugins/copilot/data.json +++ b/.obsidian/plugins/copilot/data.json @@ -6,7 +6,7 @@ "openAIOrgId": "", "huggingfaceApiKey": "", "cohereApiKey": "", - "anthropicApiKey": "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", + "anthropicApiKey": "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", "azureOpenAIApiKey": "", "azureOpenAIApiInstanceName": "", "azureOpenAIApiDeploymentName": "", @@ -24,7 +24,7 @@ "githubCopilotToken": "", "githubCopilotTokenExpiresAt": 0, "defaultChainType": "llm_chain", - "defaultModelKey": "claude-sonnet-4-6|anthropic", + "defaultModelKey": "claude-opus-4-7|anthropic", "embeddingModelKey": "openai/text-embedding-3-small|openrouterai", "temperature": 0.1, "maxTokens": 6000, diff --git a/.obsidian/plugins/copilot/main.js b/.obsidian/plugins/copilot/main.js index 31dc3c1..4ab033a 100644 --- a/.obsidian/plugins/copilot/main.js +++ b/.obsidian/plugins/copilot/main.js @@ -689,7 +689,7 @@ https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety `);super({baseURL:a.baseURL,timeout:a.timeout??6e5,httpAgent:a.httpAgent,maxRetries:a.maxRetries,fetch:a.fetch}),this.completions=new TW(this),this.chat=new d$(this),this.embeddings=new OW(this),this.files=new wB(this),this.images=new AW(this),this.audio=new JA(this),this.moderations=new DW(this),this.models=new CB(this),this.fineTuning=new Zx(this),this.vectorStores=new Hw(this),this.beta=new Xx(this),this.batches=new gB(this),this.uploads=new EB(this),this.responses=new OB(this),this.evals=new ZA(this),this._options=a,this.apiKey=r,this.organization=n,this.project=o}defaultQuery(){return this._options.defaultQuery}defaultHeaders(e){return{...super.defaultHeaders(e),"OpenAI-Organization":this.organization,"OpenAI-Project":this.project,...this._options.defaultHeaders}}authHeaders(e){return{Authorization:`Bearer ${this.apiKey}`}}stringifyQuery(e){return Ift(e,{arrayFormat:"brackets"})}};yzr=Ko;Ko.OpenAI=yzr;Ko.DEFAULT_TIMEOUT=6e5;Ko.OpenAIError=Lr;Ko.APIError=Jm;Ko.APIConnectionError=dB;Ko.APIConnectionTimeoutError=fB;Ko.APIUserAbortError=pm;Ko.NotFoundError=Aue;Ko.ConflictError=Nue;Ko.RateLimitError=jue;Ko.BadRequestError=Mue;Ko.AuthenticationError=Rue;Ko.InternalServerError=$ue;Ko.PermissionDeniedError=Iue;Ko.UnprocessableEntityError=Due;Ko.toFile=Hft;Ko.fileFromPath=T1e;Ko.Completions=TW;Ko.Chat=d$;Ko.ChatCompletionsPage=hB;Ko.Embeddings=OW;Ko.Files=wB;Ko.FileObjectsPage=MW;Ko.Images=AW;Ko.Audio=JA;Ko.Moderations=DW;Ko.Models=CB;Ko.ModelsPage=NW;Ko.FineTuning=Zx;Ko.VectorStores=Hw;Ko.VectorStoresPage=jW;Ko.VectorStoreSearchResponsesPage=$W;Ko.Beta=Xx;Ko.Batches=gB;Ko.BatchesPage=bW;Ko.Uploads=EB;Ko.Responses=OB;Ko.Evals=ZA;Ko.EvalListResponsesPage=PW;bzr=Ko});var fX,vzr=v(()=>{ra();xm();CD();_zr();Xe();fX=class extends Px{constructor(e){let{enableReasoning:r=!1,reasoningEffort:n,enablePromptCaching:o=!0,...i}=e;super(i),this.enableReasoning=r,this.reasoningEffort=n,this.enablePromptCaching=o;let a=e.configuration?.baseURL||"https://openrouter.ai/api/v1";this.isOpenRouter=a.includes("openrouter.ai"),this.openaiClient=new bzr({apiKey:e.apiKey,baseURL:a,defaultHeaders:e.configuration?.defaultHeaders,fetch:e.configuration?.fetch,dangerouslyAllowBrowser:!0})}invocationParams(e){let r=super.invocationParams(e),n=this.isOpenRouter&&this.enablePromptCaching?{...r,cache_control:{type:"ephemeral"}}:r;if(this.enableReasoning)if(this.reasoningEffort){let o=this.reasoningEffort==="minimal"?"low":this.reasoningEffort;return F(`OpenRouter reasoning enabled with effort: ${o}`),{...n,reasoning:{effort:o}}}else return F("OpenRouter reasoning enabled with max_tokens: 1024"),{...n,reasoning:{max_tokens:1024}};return n}async*_streamResponseChunks(e,r,n){let o=this.invocationParams(r),i=this.toOpenRouterMessages(e),a=await this.openaiClient.chat.completions.create({...o,messages:i,stream:!0,stream_options:{...o.stream_options??{},include_usage:!0}}),s;for await(let u of a){u.usage&&(s=u.usage);let l=u.choices?.[0],c=l?.delta;if(!l||!c)continue;let d=this.normalizeReasoningChunk(c?.reasoning),f=this.extractReasoningDetails(l),p=this.extractDeltaContent(c.content),m=this.buildMessageChunk({rawChunk:u,delta:c,content:p,finishReason:l.finish_reason,reasoningDetails:f,reasoningText:d}),h=new Mi({message:m,text:typeof m.content=="string"?m.content:"",generationInfo:{finish_reason:l.finish_reason,system_fingerprint:u.system_fingerprint,model:u.model}});yield h,h.text&&await n?.handleLLMNewToken(h.text)}if(s&&(yield this.buildUsageGenerationChunk(s)),r.signal?.aborted)throw new Error("AbortError")}toOpenRouterMessages(e){return e.map(r=>{let n=typeof r._getType=="function"?r._getType():r.role??"user",o=n==="human"?"user":n==="ai"?"assistant":n;return r.tool_call_id?{role:"tool",content:r.content,tool_call_id:r.tool_call_id}:r.additional_kwargs?.function_call?{role:o,content:r.content,function_call:r.additional_kwargs.function_call}:r.additional_kwargs?.tool_calls?{role:o,content:r.content,tool_calls:r.additional_kwargs.tool_calls}:{role:o,content:r.content}})}buildMessageChunk(e){let{rawChunk:r,delta:n,content:o,finishReason:i,reasoningText:a,reasoningDetails:s}=e,u=this.extractToolCallChunks(n.tool_calls),l={};n.function_call&&(l.function_call=n.function_call),Array.isArray(n.tool_calls)&&(l.tool_calls=n.tool_calls);let c={};a&&(c.reasoning=a),s&&s.length>0&&(c.reasoning_details=s),Object.keys(c).length>0&&(l.delta={...l.delta,...c}),s&&s.length>0&&(l.reasoning_details=s);let d=this.buildResponseMetadata(r,i);return new Hr({content:o,additional_kwargs:l,tool_call_chunks:u,response_metadata:d,id:r.id})}normalizeReasoningChunk(e){if(e){if(typeof e=="string")return e;if(Array.isArray(e))return e.map(r=>this.normalizeReasoningChunk(r)).filter(r=>!!r).join("");if(typeof e=="object"){let r=e,o=[r.output_text,r.text,r.reasoning,r.thinking,r.content].find(i=>typeof i=="string");if(typeof o=="string")return o}}}extractReasoningDetails(e){let r=e?.delta?.reasoning_details??e?.message?.reasoning_details??e?.reasoning_details;if(Array.isArray(r))return r.filter(n=>n!=null)}extractDeltaContent(e){return typeof e=="string"?e:Array.isArray(e)?e.map(r=>typeof r=="string"?r:r&&typeof r=="object"&&typeof r.text=="string"?r.text:"").join(""):""}extractToolCallChunks(e){if(Array.isArray(e))return e.map(r=>({name:r?.function?.name,args:r?.function?.arguments,id:r?.id,index:r?.index,type:"tool_call_chunk"}))}buildResponseMetadata(e,r){let n={model_provider:"openrouter"};return r&&(n.finish_reason=r),e.model&&(n.model=e.model),e.system_fingerprint&&(n.system_fingerprint=e.system_fingerprint),e.usage&&(n.usage={...e.usage},n.tokenUsage={promptTokens:e.usage.prompt_tokens,completionTokens:e.usage.completion_tokens,totalTokens:e.usage.total_tokens}),n}buildUsageGenerationChunk(e){let r={},n={},o=e.prompt_tokens_details??{};typeof o.audio_tokens=="number"&&(r.audio=o.audio_tokens),typeof o.cached_tokens=="number"&&(r.cache_read=o.cached_tokens);let i=e.completion_tokens_details??{};typeof i.audio_tokens=="number"&&(n.audio=i.audio_tokens),typeof i.reasoning_tokens=="number"&&(n.reasoning=i.reasoning_tokens);let a={input_tokens:e.prompt_tokens??0,output_tokens:e.completion_tokens??0,total_tokens:e.total_tokens??0};Object.keys(r).length>0&&(a.input_token_details=r),Object.keys(n).length>0&&(a.output_token_details=n);let s=new Hr({content:"",response_metadata:{usage:{...e}},usage_metadata:a});return new Mi({message:s,text:""})}}});function qQo(t){let e=t||globalThis.fetch;return async(r,n)=>{if(n?.body&&typeof n.body=="string")try{let o=JSON.parse(n.body),i=!1;Array.isArray(o.tools)&&(o.tools=o.tools.map(a=>{let s={};for(let[u,l]of Object.entries(a))l!=null&&(s[u]=l);return s}),i=!0),i&&(n={...n,body:JSON.stringify(o)})}catch{}return e(r,n)}}var tle,wzr=v(()=>{CD();tle=class extends Px{constructor(e){let r=e.configuration?.fetch;super({...e,useResponsesApi:!0,configuration:{...e.configuration,fetch:qQo(r)},modelKwargs:{...e.modelKwargs,text:{format:{type:"text"}}}})}}});var rDe,Szr=v(()=>{aD();Xe();ra();xm();nv();Sy();rDe=class extends Vy{constructor(e){let{modelId:r,apiKey:n,endpoint:o,streamEndpoint:i,defaultMaxTokens:a,defaultTemperature:s,defaultTopP:u,anthropicVersion:l,enableThinking:c,fetchImplementation:d,...f}=e;if(!r)throw new Error("Amazon Bedrock model identifier is required.");if(!n)throw new Error("Amazon Bedrock API key is required.");if(!o)throw new Error("Amazon Bedrock endpoint is required.");super(f);let p=typeof fetch<"u"?fetch.bind(globalThis):void 0;if(this.fetchImpl=d??p,!this.fetchImpl)throw new Error("No fetch implementation available for Amazon Bedrock requests.");f.streaming&&!i&&Ce("Amazon Bedrock streaming requested without a streaming endpoint; falling back to non-streaming mode."),this.modelName=r,this.apiKey=n,this.endpoint=o,this.streamEndpoint=i,this.defaultMaxTokens=a,this.defaultTemperature=s,this.defaultTopP=u,this.anthropicVersion=l,this.enableThinking=c??!1}_llmType(){return"amazon-bedrock"}bindTools(e){let r=Object.create(this);return r.boundTools=e,r}convertToolsToClaude(e){return e.map(r=>{let n={type:"object",properties:{}};return r.schema&&(n=Hi(r.schema)?Ii(r.schema):r.schema),{name:r.name,description:r.description||"",input_schema:n}})}extractToolCalls(e){if(!Array.isArray(e?.content))return;let r=e.content.filter(n=>n.type==="tool_use");if(r.length!==0)return r.map(n=>({id:n.id,name:n.name,args:n.input||{},type:"tool_call"}))}async _generate(e,r,n){let o=this.buildRequestBody(e,r),i=await this.fetchImpl(this.endpoint,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(o)});if(!i.ok){let m=await i.text();throw new Error(`Amazon Bedrock request failed with status ${i.status}: ${m}`)}let a=await i.json(),s=this.extractText(a),u=this.extractToolCalls(a);n&&s&&await n.handleLLMNewToken(s);let l=this.extractUsage(a),c=l?this.normaliseUsageMetadata(l):void 0,d={stopReason:a.stop_reason??a.stopReason,usage:l,rawResponse:a};return{generations:[{message:new uo({content:s,response_metadata:d,usage_metadata:c,tool_calls:u}),text:s,generationInfo:d}],llmOutput:d}}async*_streamResponseChunks(e,r={},n){if(!this.streamEndpoint){let p=await this._generate(e,r,n),m=p.generations[0]?.text??"";if(!m)return;let h=new Hr({content:m,response_metadata:p.llmOutput??{}});yield new Mi({message:h,text:m,generationInfo:p.llmOutput??{}});return}let o=this.buildRequestBody(e,r),i=`bedrock-${Date.now()}-${Math.random().toString(36).slice(2,9)}`;F(`[${i}] Starting Bedrock stream request to ${this.streamEndpoint}`);let a=await this.fetchImpl(this.streamEndpoint,{method:"POST",headers:{Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(o),signal:r?.signal});if(!a.ok){let p=await a.text();throw new Error(`Amazon Bedrock streaming request failed with status ${a.status}: ${p}`)}if(!a.body)throw new Error("Amazon Bedrock streaming response did not include a readable body.");let s=a.body.getReader(),u=new Uint8Array(0),l,c,d=!1,f=[];try{for(;;){let{value:p,done:m}=await s.read();if(m)break;if(!p)continue;let h=new Uint8Array(u.length+p.length);h.set(u),h.set(p,u.length),u=h;let{messages:y,remainingBytes:g}=this.parseEventStreamBuffer(u);u=new Uint8Array(g);for(let b of y){let _=this.safeJsonParse(b);if(!_){Ce(`[${i}] Failed to parse event JSON: ${b.slice(0,100)}...`);continue}let w=_;typeof _.bytes=="string"&&!_.type&&(w={type:"chunk",chunk:{bytes:_.bytes}});let T=await this.processStreamEvent(w,n,c,l);if(c=T.usage??c,l=T.stopReason??l,T.hasText||f.push(this.describeEvent(_)),T.deltaChunks.length>0)for(let O of T.deltaChunks)!(Array.isArray(O.message.content)&&O.message.content.length>0&&O.message.content[0]?.type==="thinking")&&O.text&&(d=!0),yield O;T.debugSummaries.length>0&&f.push(...T.debugSummaries)}}}catch(p){throw ie(`[${i}] Error during stream processing: ${p instanceof Error?p.message:String(p)}`),p}finally{s.releaseLock()}if((c||l)&&(yield this.buildTerminalMetadataChunk(l,c)),!d){Ce(`[${i}] Stream complete but no answer text yielded (only thinking or no content). Usage: ${JSON.stringify(c)}, stopReason: ${l}`),f.length>0&&F(`[${i}] Amazon Bedrock streaming produced no answer text. Sample events: ${f.slice(0,5).join(" | ")}`),Ce(`[${i}] Amazon Bedrock streaming returned no answer content. Falling back to non-streaming response.`);let p=await this._generate(e,r,n),m=p.generations[0]?.text??"";m&&(yield new Mi({message:new Hr({content:m,response_metadata:p.llmOutput??{}}),text:m,generationInfo:p.llmOutput??{}}))}}safeJsonParse(e){try{return JSON.parse(e)}catch{return null}}buildContentItemsFromDelta(e){if(!e||typeof e!="object")return null;let r=e.content_block_delta?.delta||e.contentBlockDelta?.delta||e.delta;if(!r||typeof r!="object")return null;let n=r.type;if(n==="thinking"||n==="thinking_delta"){let o=r.thinking;return typeof o=="string"&&o.length>0?[{type:"thinking",thinking:o}]:[{type:"thinking",thinking:""}]}if(n==="text_delta"||n==="text"){let o=r.text;if(typeof o=="string"&&o.length>0)return[{type:"text",text:o}]}return null}extractToolCallChunk(e){return!e||typeof e!="object"?null:e.type==="content_block_start"&&e.content_block?.type==="tool_use"?{id:e.content_block.id,index:e.index??0,name:e.content_block.name,args:""}:e.type==="content_block_delta"&&e.delta?.type==="input_json_delta"?{index:e.index??0,args:e.delta.partial_json||""}:null}async processStreamEvent(e,r,n,o){let i=[],a=n,s=o,u=!1,l=[];if(e?.type==="chunk"&&typeof e.chunk?.bytes=="string"){let c=this.decodeChunkBytes(e.chunk.bytes);for(let d of c){let f=this.safeJsonParse(d);if(!f){l.push(`Failed to parse inner payload: ${this.describePayload(d)}`);continue}let p=this.buildChunkMetadata(f),m=this.buildContentItemsFromDelta(f),h=this.extractToolCallChunk(f);if(h){let b=new Hr({content:"",response_metadata:p,tool_call_chunks:[h]}),_=new Mi({message:b,text:"",generationInfo:p});i.push(_),u=!0}else if(m&&m.length>0){let b=m[0]?.type==="thinking",_=b?m[0].thinking||"":m[0]?.text||"",w={};b&&_&&(w.delta={reasoning:_});let T=new Hr({content:m,response_metadata:p,...Object.keys(w).length>0?{additional_kwargs:w}:{}}),O=new Mi({message:T,text:_,generationInfo:p});i.push(O),u=!0,r&&_&&await r.handleLLMNewToken(_)}else{let b=this.extractStreamText(f);if(b){let _=new Hr({content:b,response_metadata:p}),w=new Mi({message:_,text:b,generationInfo:p});i.push(w),u=!0,r&&await r.handleLLMNewToken(b)}else if(f.type==="content_block_delta"&&f.delta?.type!=="input_json_delta"){let _=`No content in content_block_delta event: ${this.describeEvent(f)}`;l.push(_),Ce(`processStreamEvent: ${_}`)}}let y=this.extractUsage(f);y&&(a=y);let g=this.extractStopReason(f);g&&(s=g)}}else{let c=this.buildChunkMetadata(e),d=this.buildContentItemsFromDelta(e),f=this.extractToolCallChunk(e);if(f){let h=new Hr({content:"",response_metadata:c,tool_call_chunks:[f]}),y=new Mi({message:h,text:"",generationInfo:c});i.push(y),u=!0}else if(d&&d.length>0){let h=d[0]?.type==="thinking",y=h?d[0].thinking||"":d[0]?.text||"",g={};h&&y&&(g.delta={reasoning:y});let b=new Hr({content:d,response_metadata:c,...Object.keys(g).length>0?{additional_kwargs:g}:{}}),_=new Mi({message:b,text:y,generationInfo:c});i.push(_),u=!0,r&&y&&await r.handleLLMNewToken(y)}else{let h=this.extractStreamText(e);if(h){let y=new Hr({content:h,response_metadata:c}),g=new Mi({message:y,text:h,generationInfo:c});i.push(g),u=!0,r&&await r.handleLLMNewToken(h)}}let p=this.extractUsage(e);p&&(a=p);let m=this.extractStopReason(e);m&&(s=m)}return{deltaChunks:i,usage:a,stopReason:s,hasText:u,debugSummaries:l}}describeEvent(e){if(!e)return"";let r=typeof e.type=="string"?e.type:"unknown",n=Object.keys(e).slice(0,6).join(","),o=this.stringifyForLog(e);return`${r} {${n}} -> ${o}`}describePayload(e){return e?e.length<=200?e:`${e.slice(0,200)}\u2026 (len=${e.length})`:""}stringifyForLog(e){try{let r=this.sanitiseForLog(e),n=JSON.stringify(r);return n?n.length>400?`${n.slice(0,400)}\u2026 (len=${n.length})`:n:""}catch{return""}}sanitiseForLog(e){if(!e||typeof e!="object")return e;if(Array.isArray(e))return e.slice(0,5).map(i=>this.sanitiseForLog(i));let r=e,n={},o=Object.entries(r);for(let i=0;i200?a==="bytes"||a==="chunk"||a==="chunk_bytes"?n[a]=``:n[a]=`${s.slice(0,200)}\u2026 (len=${s.length})`:n[a]=this.sanitiseForLog(s)}return n}decodeChunkBytes(e){let r=this.decodeBase64ToUint8Array(e);if(!r||r.length===0)return Ce("decodeChunkBytes: Failed to decode base64 or empty bytes"),[];let n=this.findFirstNonWhitespaceByte(r);if(n===123||n===91){let a=this.decodeUtf8(r);return this.splitJsonLines(a)}let o=this.decodeEventStreamMessages(r);if(o.length>0)return o;Ce("decodeChunkBytes: EventStream decoding failed, falling back to plain UTF-8");let i=this.decodeUtf8(r);return this.splitJsonLines(i)}decodeBase64ToUint8Array(e){try{if(typeof Buffer<"u")return new Uint8Array(Buffer.from(e,"base64"));if(typeof atob=="function"){let r=atob(e),n=new Uint8Array(r.length);for(let o=0;or.trim()).filter(r=>r.length>0):[]}parseEventStreamBuffer(e){let r=[];if(e.length<12)return{messages:r,remainingBytes:e};let n=new DataView(e.buffer,e.byteOffset,e.byteLength),o=0;for(;o+12<=e.length;){let a=n.getUint32(o,!1),s=n.getUint32(o+4,!1);if(o+a>e.length)break;if(a<=0||s<0||s+12>a){Ce(`parseEventStreamBuffer: Invalid message structure at offset ${o}: totalLength=${a}, headersLength=${s}`);break}let u=o+12+s,l=o+a-4;if(u>l||l>e.length){Ce(`parseEventStreamBuffer: Invalid payload bounds at offset ${o}`);break}if(u0&&r.push(d)}if(o+=a,a===0)break}let i=o0)return i;let n=[e.text,e.outputText,e.completion,e.resultText,e.delta];for(let i of n)if(typeof i=="string"&&i.length>0)return i;let o=[e.delta?.text,e.delta?.output_text,e.delta?.content,e.contentBlockDelta?.delta?.text,e.contentBlockDelta?.delta?.output_text,e.contentBlockDelta?.delta?.content,e.content_block_delta?.delta?.text,e.content_block_delta?.delta?.output_text,e.content_block_delta?.delta?.content,e.message?.content,e.messageStop?.message?.content,e.message_stop?.message?.content,e.content];for(let i of o){let a=this.extractTextFromCandidate(i);if(a)return a}return null}extractTextFromCandidate(e){if(!e)return null;if(typeof e=="string")return e.length>0?e:null;if(Array.isArray(e)){let r=e.map(n=>{if(typeof n=="string")return n;if(n&&typeof n=="object"){if(typeof n.text=="string")return n.text;if(typeof n.value=="string")return n.value;if(Array.isArray(n.content))return n.content.map(o=>typeof o?.text=="string"?o.text:"").join("")}return""}).join("");return r.length>0?r:null}if(typeof e=="object"){let r=e;if(typeof r.text=="string")return r.text.length>0?r.text:null;if(r.text&&typeof r.text=="object"){let n=this.extractTextFromCandidate(r.text);if(n)return n}if(typeof r.value=="string")return r.value.length>0?r.value:null;if(Array.isArray(r.content))return this.extractTextFromCandidate(r.content);if(r.delta){let n=this.extractTextFromCandidate(r.delta);if(n)return n}if(r.message&&typeof r.message=="object"){let n=this.extractTextFromCandidate(r.message);if(n)return n}}return null}extractUsage(e){if(!(!e||typeof e!="object")){if(e.usage&&typeof e.usage=="object")return e.usage;if(e.metrics&&typeof e.metrics=="object")return e.metrics;if(e["amazon-bedrock-invocationMetrics"]&&typeof e["amazon-bedrock-invocationMetrics"]=="object")return e["amazon-bedrock-invocationMetrics"];if(e.messageStop&&typeof e.messageStop=="object")return this.extractUsage(e.messageStop);if(e.message_stop&&typeof e.message_stop=="object")return this.extractUsage(e.message_stop)}}extractStopReason(e){if(!e||typeof e!="object")return;let r=e.stop_reason||e.stopReason||e.completionReason||e.completion_reason||e.reason||e.messageStop?.stopReason||e.message_stop?.stop_reason||(e.type==="message_stop"?e.reason:void 0);return typeof r=="string"?r:void 0}buildTerminalMetadataChunk(e,r){let n=r?this.normaliseUsageMetadata(r):void 0,o={provider:"amazon-bedrock"};e&&(o.stop_reason=e),r&&(o.usage=r);let i=new Hr({content:"",response_metadata:o,usage_metadata:n});return new Mi({message:i,text:"",generationInfo:o})}normaliseUsageMetadata(e){let r=this.coerceNumber(e.inputTokens)??this.coerceNumber(e.input_tokens)??this.coerceNumber(e.inputTokenCount)??this.coerceNumber(e.promptTokens)??this.coerceNumber(e.prompt_tokens)??0,n=this.coerceNumber(e.outputTokens)??this.coerceNumber(e.output_tokens)??this.coerceNumber(e.outputTokenCount)??this.coerceNumber(e.completionTokens)??this.coerceNumber(e.completion_tokens)??0,o=this.coerceNumber(e.totalTokens)??this.coerceNumber(e.total_tokens)??r+n;return{input_tokens:r,output_tokens:n,total_tokens:o}}coerceNumber(e){if(typeof e=="number")return e;if(typeof e=="string"&&e.trim().length>0){let r=Number(e);return Number.isFinite(r)?r:void 0}}convertImageContent(e){try{let r=e.match(/^data:([^;]+);base64,(.+)$/);if(!r)return null;let[,n,o]=r;return!n||!o||!n.startsWith("image/")?null:{type:"image",source:{type:"base64",media_type:n,data:o}}}catch(r){return ie("Error converting image content:",r),null}}buildRequestBody(e,r){let n=[],o=[];e.forEach(l=>{let c=l._getType();if(c==="system"){let p=this.normaliseMessageContent(l),m=typeof p=="string"?p:"";m&&o.push(m);return}if(c==="tool"){let p=l,m=typeof p.content=="string"?p.content:JSON.stringify(p.content);n.push({role:"user",content:[{type:"tool_result",tool_use_id:p.tool_call_id,content:m}]});return}if(c==="ai"){let m=l.tool_calls;if(m&&m.length>0){let h=[],y=this.normaliseMessageContent(l);typeof y=="string"&&y&&h.push({type:"text",text:y});for(let g of m)h.push({type:"tool_use",id:g.id||`tool_${Date.now()}`,name:g.name,input:g.args});h.length>0&&n.push({role:"assistant",content:h});return}}let d=this.normaliseMessageContent(l);if(!d)return;let f=[];if(typeof d=="string")f.push({type:"text",text:d});else if(Array.isArray(d))for(let p of d)if(p.type==="text"&&typeof p.text=="string")f.push({type:"text",text:p.text});else if(p.type==="image_url"&&p.image_url?.url){let m=this.convertImageContent(p.image_url.url);m&&f.push(m)}else p.type==="image"&&p.source&&f.push(p);f.length>0&&n.push({role:c==="ai"?"assistant":"user",content:f})});let i=r?.maxTokens??this.defaultMaxTokens,a=r?.temperature??this.defaultTemperature,s=r?.topP??this.defaultTopP,u={messages:n};return this.boundTools&&this.boundTools.length>0&&(u.tools=this.convertToolsToClaude(this.boundTools)),o.length>0&&(u.system=o.join(` -`)),i!==void 0&&(u.max_tokens=i),this.anthropicVersion&&(u.anthropic_version=this.anthropicVersion),this.enableThinking?(u.thinking={type:"enabled",budget_tokens:2048},u.temperature=1,F("[BedrockChatModel] Enabled thinking mode for Claude model with temperature=1")):a!==void 0&&(u.temperature=a),s!==void 0&&(u.top_p=s),u}normaliseMessageContent(e){let{content:r}=e;return typeof r=="string"?r:Array.isArray(r)?r.some(o=>typeof o=="object"&&o!==null&&(o.type==="image_url"||o.type==="image"))?r.map(o=>{if(typeof o=="string")return{type:"text",text:o};if(typeof o=="object"&&o!==null){if(o.type==="text"||o.type==="image_url"||o.type==="image")return o;if("text"in o&&typeof o.text=="string")return{type:"text",text:o.text};if("content"in o&&typeof o.content=="string")return{type:"text",text:o.content}}return null}).filter(o=>o!==null):r.map(o=>{if(typeof o=="string")return o;if(typeof o=="object"&&o!==null){if("text"in o&&typeof o.text=="string")return o.text;if("content"in o&&typeof o.content=="string")return o.content}return""}).join(""):typeof r=="object"&&r!==null&&"text"in r?r.text??"":""}extractText(e){return typeof e?.outputText=="string"?e.outputText:Array.isArray(e?.content)?e.content.map(r=>{if(!r)return"";if(typeof r=="string")return r;if(typeof r=="object"){if(typeof r.text=="string")return r.text;if(r.text&&typeof r.text=="object"&&"text"in r.text)return r.text.text??""}return""}).join(""):typeof e?.completion=="string"?e.completion:typeof e?.resultText=="string"?e.resultText:""}}});function rmt(t){return t instanceof B0||t instanceof Error&&t.name==="AuthCancelledError"}var B0,nmt=v(()=>{B0=class t extends Error{constructor(r="Authentication cancelled by user."){super(r);this.name="AuthCancelledError";Object.setPrototypeOf(this,t.prototype)}}});var rle,Czr,BQo,UQo,zQo,omt,VQo,HQo,xzr,WQo,g$,nDe=v(()=>{Qe();BC();rle=require("obsidian");nmt();Czr="Iv1.b507a08c87ecfe98",BQo="https://github.com/login/device/code",UQo="https://github.com/login/oauth/access_token",zQo="https://api.github.com/copilot_internal/v2/token",omt="https://api.githubcopilot.com",VQo=`${omt}/models`,HQo=60*1e3,xzr=60*60*1e3,WQo=3,g$=class t{constructor(){this.abortController=null;this.refreshPromise=null;this.refreshAttempts=0;this.modelPolicyTermsCache=new Map;this.authGeneration=0}static getInstance(){return t.instance||(t.instance=new t),t.instance}getAuthState(){let e=ee(),r=!!e.githubCopilotAccessToken,n=!!e.githubCopilotToken,o=e.githubCopilotTokenExpiresAt,a=!(typeof o=="number"&&o>0)||o0?n.interval:5}}async pollForAccessToken(e,r,n,o){let i=this.authGeneration;this.abortPolling();let a=new AbortController;this.abortController=a;let s=Date.now()+n*1e3,u=0;try{for(;Date.now()0)||r=WQo)throw this.refreshAttempts=0,new Error("Failed to refresh Copilot token after multiple attempts. Please try reconnecting.");return this.refreshPromise?this.refreshPromise:(this.refreshAttempts++,this.refreshPromise=this.fetchCopilotToken().finally(()=>{this.refreshPromise=null}),this.refreshPromise)}buildCopilotHeaders(e){return{"Content-Type":"application/json",Authorization:`Bearer ${e}`,"User-Agent":"GitHubCopilotChat/0.38.2026022001","Editor-Version":"vscode/1.110.0","Editor-Plugin-Version":"copilot-chat/0.38.2026022001","Copilot-Integration-Id":"vscode-chat","Openai-Intent":"conversation-panel","X-GitHub-Api-Version":"2025-05-01"}}abortPolling(){this.abortController&&(this.abortController.abort(),this.abortController=null)}resetAuth(){this.authGeneration++,this.abortPolling(),this.refreshPromise=null,this.refreshAttempts=0,this.modelPolicyTermsCache.clear(),A_({githubCopilotAccessToken:"",githubCopilotToken:"",githubCopilotTokenExpiresAt:0})}async listModels(){let e=async i=>await(0,rle.requestUrl)({url:VQo,method:"GET",headers:{...this.buildCopilotHeaders(i),Accept:"application/json","Openai-Intent":"model-access"},throw:!1}),r=await this.getValidCopilotToken(),n=await e(r);if(n.status===401&&(this.clearCopilotToken(),r=await this.getValidCopilotToken(),n=await e(r)),n.status!==200)throw new Error(`Failed to list models: ${n.status}`);let o=this.getRequestUrlJson(n);return this.modelPolicyTermsCache.clear(),o.data?.forEach(i=>{i.policy?.terms&&this.modelPolicyTermsCache.set(i.id,i.policy.terms)}),o}getPolicyTerms(e){return this.modelPolicyTermsCache.get(e)}clearCopilotToken(){A_({githubCopilotToken:"",githubCopilotTokenExpiresAt:0})}buildCopilotRequestHeaders(e){return this.buildCopilotHeaders(e)}invalidateCopilotToken(){this.clearCopilotToken()}getRequestUrlJson(e){if(typeof e.json=="string")try{return JSON.parse(e.json)}catch{return e.json}return e.json}parseCopilotTokenExpiresAt(e){if(!e||typeof e!="object")return Date.now()+xzr;let r=e,n=this.parseExpiresAtValue(r.expires_at);if(n!==null)return n;let o=r.expires_in,i=typeof o=="number"?o:typeof o=="string"?Number(o):Number.NaN;return Number.isFinite(i)&&i>0?Date.now()+i*1e3:Date.now()+xzr}parseExpiresAtValue(e){if(typeof e=="number")return!Number.isFinite(e)||e<=0?null:e>1e12?e:e*1e3;if(typeof e=="string"){let r=e.trim();if(!r)return null;let n=Number(r);if(Number.isFinite(n)&&n>0)return n>1e12?n:n*1e3;let o=Date.parse(r);if(!Number.isNaN(o)&&o>0)return o}return null}delay(e,r){return r?r.aborted?Promise.reject(new B0):new Promise((n,o)=>{let i=()=>{clearTimeout(a),o(new B0)},a=setTimeout(()=>{r.removeEventListener("abort",i),n()},e);r.addEventListener("abort",i,{once:!0})}):new Promise(n=>setTimeout(n,e))}}});function KQo(t){return typeof t=="string"?t:t==null?"":Array.isArray(t)?t.map(e=>typeof e=="string"?e:e&&typeof e=="object"&&typeof e.text=="string"?e.text:"").join(""):typeof t=="object"&&typeof t.text=="string"?t.text:""}var JQo,oDe,Tzr=v(()=>{CD();nDe();Dt();JQo=4;oDe=class t extends iA{constructor(r){let{fetchImplementation:n,configuration:o,apiKey:i,...a}=r,s=g$.getInstance(),u=n??o?.fetch??fetch,l=t.buildAuthedFetch(s,u);super({...a,apiKey:i||"copilot-dynamic-token",streamUsage:!1,configuration:{...o??{},baseURL:o?.baseURL??omt,fetch:l}});this.lc_serializable=!1;this.lc_namespace=["langchain","chat_models","github_copilot"]}static buildAuthedFetch(r,n){return async(o,i={})=>{let a=typeof o=="string"?o:typeof Request<"u"&&o instanceof Request?o.url:o.toString(),s=async c=>{let d=r.buildCopilotRequestHeaders(c),f=new Headers(i.headers);for(let[p,m]of Object.entries(d))f.set(p,m);return n(a,{...i,headers:f})},u=await r.getValidCopilotToken(),l=await s(u);if(l.status===401){try{await l.body?.cancel()}catch{}r.invalidateCopilotToken(),u=await r.getValidCopilotToken(),l=await s(u)}return l}}_llmType(){return"github-copilot"}_convertCompletionsDeltaToBaseMessageChunk(r,n,o){return!r.role&&!o&&(o="assistant"),r.content=KQo(r.content),super._convertCompletionsDeltaToBaseMessageChunk(r,n,o)}async getNumTokens(r){let n=tF(r);return n?Math.ceil(n.length/JQo):0}}});var imt={};En(imt,{default:()=>U0,normalizeAzureUrl:()=>Ezr});function Ezr(t){if(!t)return{baseUrl:void 0,apiVersion:void 0};let e;try{e=new URL(t)}catch{return{baseUrl:t,apiVersion:void 0}}let r=e.searchParams.get("api-version")||void 0;e.search="";let n=e.toString().replace(/\/+$/,"");return n=n.replace(/\/(chat\/completions|embeddings)$/,""),{baseUrl:n,apiVersion:r}}var Ozr,GQo,pd,U0,LW=v(()=>{na();gr();BC();Xe();id();Qe();Dt();uie();Hvr();att();rv();Jvr();btt();wwr();oUr();Dtt();CD();aUr();OS();Ozr=require("obsidian");vzr();wzr();Szr();Tzr();sV.prototype.getNumTokens=async t=>{let e=typeof t=="string"?t:t.map(r=>typeof r=="string"?r:r.text??"").join("");return Math.ceil(e.length/4)};GQo={openai:Px,"azure openai":Px,anthropic:tut,cohereai:ott,google:gtt,xai:Oft,openrouterai:fX,ollama:Att,"lm-studio":fX,groq:Rut,"3rd party (openai-format)":Px,siliconflow:Px,"copilot-plus":fX,mistralai:Tft,deepseek:rut,"amazon-bedrock":rDe,"github-copilot":oDe};pd=class pd{constructor(){this.providerApiKeyMap={openai:()=>ee().openAIApiKey,google:()=>ee().googleApiKey,"azure openai":()=>ee().azureOpenAIApiKey,anthropic:()=>ee().anthropicApiKey,cohereai:()=>ee().cohereApiKey,openrouterai:()=>ee().openRouterAiApiKey,groq:()=>ee().groqApiKey,xai:()=>ee().xaiApiKey,ollama:()=>"default-key","lm-studio":()=>"default-key","3rd party (openai-format)":()=>"default-key","copilot-plus":()=>ee().plusLicenseKey,mistralai:()=>ee().mistralApiKey,deepseek:()=>ee().deepseekApiKey,"amazon-bedrock":()=>ee().amazonBedrockApiKey,siliconflow:()=>ee().siliconflowApiKey,"github-copilot":()=>ee().githubCopilotToken||ee().githubCopilotAccessToken};this.buildModelMap(),ml(()=>{this.buildModelMap(),this.validateCurrentModel()})}static getInstance(){return pd.instance||(pd.instance=new pd),pd.instance}getTemperatureForModel(e,r,n){if(!e.isThinkingEnabled)return e.isOSeries||e.isGPT5?pd.REASONING_MODEL_TEMPERATURE:r.temperature??n.temperature}async getModelConfig(e){let r=ee(),n=e.name,o=kJ(n),{isThinkingEnabled:i}=o,a=this.getTemperatureForModel(o,e,r),s=e.maxTokens??r.maxTokens,u={modelName:n,streaming:e.stream??!0,maxRetries:3,maxConcurrency:3,enableCors:e.enableCors,...!i&&a!==void 0?{temperature:a}:{}},c={openai:{modelName:n,apiKey:await kn(e.apiKey||r.openAIApiKey),configuration:{baseURL:e.baseUrl,fetch:e.enableCors?hl:void 0,organization:await kn(e.openAIOrgId||r.openAIOrgId)},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)},anthropic:{anthropicApiKey:await kn(e.apiKey||r.anthropicApiKey),model:n,anthropicApiUrl:e.baseUrl,clientOptions:{defaultHeaders:{"anthropic-dangerous-direct-browser-access":"true"},fetch:e.enableCors?hl:void 0},...i&&{thinking:{type:"enabled",budget_tokens:pd.ANTHROPIC_THINKING_BUDGET_TOKENS}}},"azure openai":await(async()=>{let m=Ezr(e.baseUrl);return{modelName:e.baseUrl?n:e.azureOpenAIApiDeploymentName||r.azureOpenAIApiDeploymentName,apiKey:await kn(e.apiKey||r.azureOpenAIApiKey),configuration:{baseURL:m.baseUrl||`https://${e.azureOpenAIApiInstanceName||r.azureOpenAIApiInstanceName}.openai.azure.com/openai/deployments/${e.azureOpenAIApiDeploymentName||r.azureOpenAIApiDeploymentName}`,defaultQuery:{"api-version":m.apiVersion||e.azureOpenAIApiVersion||r.azureOpenAIApiVersion||"2024-05-01-preview"},defaultHeaders:{"Content-Type":"application/json","api-key":await kn(e.apiKey||r.azureOpenAIApiKey)},fetch:e.enableCors?hl:void 0},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)}})(),cohereai:{apiKey:await kn(e.apiKey||r.cohereApiKey),model:n},google:{apiKey:await kn(e.apiKey||r.googleApiKey),model:n,safetySettings:[{category:dV.HARM_CATEGORY_SEXUALLY_EXPLICIT,threshold:fV.BLOCK_NONE},{category:dV.HARM_CATEGORY_HATE_SPEECH,threshold:fV.BLOCK_NONE},{category:dV.HARM_CATEGORY_DANGEROUS_CONTENT,threshold:fV.BLOCK_NONE},{category:dV.HARM_CATEGORY_HARASSMENT,threshold:fV.BLOCK_NONE}],baseUrl:e.baseUrl},xai:{apiKey:await kn(e.apiKey||r.xaiApiKey),model:n},openrouterai:{modelName:n,apiKey:await kn(e.apiKey||r.openRouterAiApiKey),configuration:{baseURL:e.baseUrl||"https://openrouter.ai/api/v1",fetch:e.enableCors?hl:void 0,defaultHeaders:{"HTTP-Referer":"https://obsidiancopilot.com","X-Title":"Obsidian Copilot"}},enableReasoning:e.capabilities?.includes("reasoning")??!1,reasoningEffort:e.capabilities?.includes("reasoning")&&e.reasoningEffort?e.reasoningEffort:void 0,enablePromptCaching:e.enablePromptCaching??!0},groq:{apiKey:await kn(e.apiKey||r.groqApiKey),model:n},ollama:{model:n,baseUrl:e.baseUrl||"http://localhost:11434",headers:{Authorization:`Bearer ${await kn(e.apiKey||"default-key")}`},think:e.capabilities?.includes("reasoning")??!1,repeatPenalty:1.1,numCtx:e.numCtx??UJ},"lm-studio":{modelName:n,apiKey:e.apiKey||"default-key",streamUsage:e.streamUsage??!1,configuration:{baseURL:e.baseUrl||"http://localhost:1234/v1",fetch:e.enableCors?hl:void 0},enableReasoning:e.capabilities?.includes("reasoning")??!1,reasoningEffort:e.capabilities?.includes("reasoning")&&e.reasoningEffort?e.reasoningEffort:void 0},"3rd party (openai-format)":{modelName:n,apiKey:await kn(e.apiKey||r.openAIApiKey),streamUsage:e.streamUsage??!1,configuration:{baseURL:e.baseUrl,fetch:e.enableCors?hl:void 0,defaultHeaders:{"dangerously-allow-browser":"true"}},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)},siliconflow:{modelName:n,apiKey:await kn(e.apiKey||r.siliconflowApiKey),configuration:{baseURL:e.baseUrl||qC.siliconflow.host,fetch:e.enableCors?hl:void 0},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)},"copilot-plus":{modelName:n,apiKey:await kn(r.plusLicenseKey),configuration:{baseURL:i1,fetch:hl}},mistralai:{model:n,apiKey:await kn(e.apiKey||r.mistralApiKey),serverURL:e.baseUrl},deepseek:{modelName:n,apiKey:await kn(e.apiKey||r.deepseekApiKey),configuration:{baseURL:e.baseUrl||qC.deepseek.host,fetch:e.enableCors?hl:void 0}},"amazon-bedrock":{},"github-copilot":{modelName:n,fetchImplementation:e.enableCors?Rge:void 0}}[e.provider]||{};e.provider==="amazon-bedrock"&&(c=await this.buildBedrockConfig(e,n,r,s,a));let d=this.getProviderSpecificParams(e.provider,e);return{...u,...c,...d,...{maxTokens:s}}}getOpenAISpecialConfig(e,r,n,o){let i=ee(),a=kJ(e),s=this.getTemperatureForModel(a,o||{},i),u={maxTokens:r,temperature:s};if((a.isOSeries||a.isGPT5)&&o?.reasoningEffort&&(u.reasoning={effort:o.reasoningEffort},a.isGPT5&&o?.verbosity&&o?.provider!=="azure openai")){let l=o.verbosity;u.text={verbosity:l}}return u}async buildBedrockConfig(e,r,n,o,i){let a=e.apiKey||n.amazonBedrockApiKey;if(!a)throw new Error("Amazon Bedrock API key is not configured. Provide a key in Settings > API Keys or the model definition.");let s=await kn(a),u=e.bedrockRegion?.trim(),l=n.amazonBedrockRegion?.trim(),c=u||l||"us-east-1",d=e.baseUrl?.trim(),p=(d?d.replace(/\/+$/,""):void 0)||`https://bedrock-runtime.${c}.amazonaws.com`,m=encodeURIComponent(r),h=`${p}/model/${m}/invoke`,y=`${p}/model/${m}/invoke-with-response-stream`,g=e.enableCors?hl:void 0,_=/(^|\.)anthropic\./.test(r)?"bedrock-2023-05-31":void 0,w=e.capabilities?.includes("reasoning")??!1;return{modelName:r,modelId:r,apiKey:s,endpoint:h,streamEndpoint:y,defaultMaxTokens:o,defaultTemperature:i,defaultTopP:e.topP,anthropicVersion:_,enableThinking:w,fetchImplementation:g,streaming:e.stream??!0}}getProviderSpecificParams(e,r){let n={};return r.topP!==void 0&&["openai","azure openai","anthropic","google","openrouterai","ollama","lm-studio","3rd party (openai-format)","mistralai","deepseek","siliconflow"].includes(e)&&(n.topP=r.topP),r.frequencyPenalty!==void 0&&["openai","azure openai","openrouterai","ollama","lm-studio","3rd party (openai-format)","mistralai","deepseek","siliconflow"].includes(e)&&(n.frequencyPenalty=r.frequencyPenalty),n}buildModelMap(){let e=ee().activeModels;pd.modelMap={};let r=pd.modelMap;(e??aO).forEach(o=>{if(o.enabled){if(!Object.values(Mb).contains(o.provider)){console.warn(`Unknown provider: ${o.provider} for model: ${o.name}`);return}let i=this.getProviderConstructor(o),a=this.hasProviderCredentials(o),s=no(o);r[s]={hasApiKey:a,AIConstructor:i,vendor:o.provider}}})}hasProviderCredentials(e){if(e.provider==="amazon-bedrock"){let n=ee();return!!(e.apiKey||n.amazonBedrockApiKey)}let r=this.providerApiKeyMap[e.provider];return r?!!(e.apiKey||r()):!!e.apiKey}getProviderConstructor(e){let r=GQo[e.provider];if(!r)throw console.warn(`Unknown provider: ${e.provider} for model: ${e.name}`),new Error(`Unknown provider: ${e.provider} for model: ${e.name}`);return r}getChatModel(){if(!pd.chatModel)throw new Error("No valid chat model available. Please check your API key settings.");return pd.chatModel}isModelConfigValid(e,r){let n=no(e),o=pd.modelMap[n];return!(!o||!o.hasApiKey||e.plusExclusive&&!Dbr())}resolveModelForTemperatureOverride(){let e=ee();try{let r=j_();if(r){let n=PS(r,e.activeModels);if(this.isModelConfigValid(n,e))return n}}catch{}for(let r of e.activeModels)if(r.enabled&&!r.believerExclusive&&this.isModelConfigValid(r,e))return r;throw new Error("No valid chat model available for temperature override. Please check your API key settings and ensure at least one model is properly configured.")}async getChatModelWithTemperature(e){let n={...this.resolveModelForTemperatureOverride(),temperature:e};return await this.createModelInstance(n)}async setChatModel(e){try{let r=await this.createModelInstance(e);pd.chatModel=r,kJ(e.name).isGPT5&&(e.provider==="openai"||e.provider==="3rd party (openai-format)")&&F(`Chat model set with Responses API for GPT-5: ${e.name}`)}catch(r){throw ie(r),r}}async createModelInstance(e){let r=no(e),n=pd.modelMap[r];if(!n)throw new Error(`No model found for: ${r}`);if(!n.hasApiKey){let u=`API key is not provided for the model: ${r}.`;throw e.provider==="copilot-plus"?new y4("Copilot Plus license key is not configured. Please enter your license key in the Copilot Plus section at the top of Basic Settings."):new nte(u)}let o=await this.getModelConfig(e),i=kJ(e.name),a={...o};if(i.isGPT5&&(n.vendor==="openai"||n.vendor==="3rd party (openai-format)")&&(a.useResponsesApi=!0,F(`Enabling Responses API for GPT-5 model: ${e.name} (${n.vendor})`)),e.provider==="lm-studio"&&e.useResponsesApi!==!1){let u=new tle(a);return F(`[ChatModelManager] Using Responses API for LM Studio model: ${e.name}`),u}return new n.AIConstructor(a)}validateChatModel(e){return e!=null}estimateTokens(e){return e?Math.ceil(e.length/4):0}async countTokens(e){return pd.chatModel?.getNumTokens(e)??this.estimateTokens(e)}validateCurrentModel(){if(!pd.chatModel)return;let e=j_();if(!e)return;pd.modelMap[e]?.hasApiKey||(pd.chatModel=null,F("Failed to reinitialize model due to missing API key"))}async ping(e){let r=async n=>{let o={...e,enableCors:n},i=await this.getModelConfig(o),{streaming:a,maxTokens:s,maxCompletionTokens:u,...l}=i,c=kJ(e.name),f={maxTokens:c.isThinkingEnabled?4096:30},p={...l,...f};c.isGPT5&&(e.provider==="openai"||e.provider==="3rd party (openai-format)")&&(p.useResponsesApi=!0),await(e.provider==="lm-studio"&&e.useResponsesApi!==!1?new tle(p):new(this.getProviderConstructor(o))(p)).invoke([{role:"user",content:"hello"}],{timeout:8e3})};try{return await r(!1),!0}catch(n){console.log("First ping attempt failed, trying with CORS...");try{return await r(!0),new Ozr.Notice("Connection successful, but requires CORS to be enabled. Please enable CORS for this model once you add it above."),!0}catch(o){let i=` +`)),i!==void 0&&(u.max_tokens=i),this.anthropicVersion&&(u.anthropic_version=this.anthropicVersion),this.enableThinking?(u.thinking=(m=>m&&parseInt(m[1],10)>=7?{type:"adaptive"}:{type:"enabled",budget_tokens:2048})(String(this.modelName||"").match(/claude-opus-4-(\d+)/)),u.temperature=1,F("[BedrockChatModel] Enabled thinking mode for Claude model with temperature=1")):a!==void 0&&(u.temperature=a),s!==void 0&&(u.top_p=s),u}normaliseMessageContent(e){let{content:r}=e;return typeof r=="string"?r:Array.isArray(r)?r.some(o=>typeof o=="object"&&o!==null&&(o.type==="image_url"||o.type==="image"))?r.map(o=>{if(typeof o=="string")return{type:"text",text:o};if(typeof o=="object"&&o!==null){if(o.type==="text"||o.type==="image_url"||o.type==="image")return o;if("text"in o&&typeof o.text=="string")return{type:"text",text:o.text};if("content"in o&&typeof o.content=="string")return{type:"text",text:o.content}}return null}).filter(o=>o!==null):r.map(o=>{if(typeof o=="string")return o;if(typeof o=="object"&&o!==null){if("text"in o&&typeof o.text=="string")return o.text;if("content"in o&&typeof o.content=="string")return o.content}return""}).join(""):typeof r=="object"&&r!==null&&"text"in r?r.text??"":""}extractText(e){return typeof e?.outputText=="string"?e.outputText:Array.isArray(e?.content)?e.content.map(r=>{if(!r)return"";if(typeof r=="string")return r;if(typeof r=="object"){if(typeof r.text=="string")return r.text;if(r.text&&typeof r.text=="object"&&"text"in r.text)return r.text.text??""}return""}).join(""):typeof e?.completion=="string"?e.completion:typeof e?.resultText=="string"?e.resultText:""}}});function rmt(t){return t instanceof B0||t instanceof Error&&t.name==="AuthCancelledError"}var B0,nmt=v(()=>{B0=class t extends Error{constructor(r="Authentication cancelled by user."){super(r);this.name="AuthCancelledError";Object.setPrototypeOf(this,t.prototype)}}});var rle,Czr,BQo,UQo,zQo,omt,VQo,HQo,xzr,WQo,g$,nDe=v(()=>{Qe();BC();rle=require("obsidian");nmt();Czr="Iv1.b507a08c87ecfe98",BQo="https://github.com/login/device/code",UQo="https://github.com/login/oauth/access_token",zQo="https://api.github.com/copilot_internal/v2/token",omt="https://api.githubcopilot.com",VQo=`${omt}/models`,HQo=60*1e3,xzr=60*60*1e3,WQo=3,g$=class t{constructor(){this.abortController=null;this.refreshPromise=null;this.refreshAttempts=0;this.modelPolicyTermsCache=new Map;this.authGeneration=0}static getInstance(){return t.instance||(t.instance=new t),t.instance}getAuthState(){let e=ee(),r=!!e.githubCopilotAccessToken,n=!!e.githubCopilotToken,o=e.githubCopilotTokenExpiresAt,a=!(typeof o=="number"&&o>0)||o0?n.interval:5}}async pollForAccessToken(e,r,n,o){let i=this.authGeneration;this.abortPolling();let a=new AbortController;this.abortController=a;let s=Date.now()+n*1e3,u=0;try{for(;Date.now()0)||r=WQo)throw this.refreshAttempts=0,new Error("Failed to refresh Copilot token after multiple attempts. Please try reconnecting.");return this.refreshPromise?this.refreshPromise:(this.refreshAttempts++,this.refreshPromise=this.fetchCopilotToken().finally(()=>{this.refreshPromise=null}),this.refreshPromise)}buildCopilotHeaders(e){return{"Content-Type":"application/json",Authorization:`Bearer ${e}`,"User-Agent":"GitHubCopilotChat/0.38.2026022001","Editor-Version":"vscode/1.110.0","Editor-Plugin-Version":"copilot-chat/0.38.2026022001","Copilot-Integration-Id":"vscode-chat","Openai-Intent":"conversation-panel","X-GitHub-Api-Version":"2025-05-01"}}abortPolling(){this.abortController&&(this.abortController.abort(),this.abortController=null)}resetAuth(){this.authGeneration++,this.abortPolling(),this.refreshPromise=null,this.refreshAttempts=0,this.modelPolicyTermsCache.clear(),A_({githubCopilotAccessToken:"",githubCopilotToken:"",githubCopilotTokenExpiresAt:0})}async listModels(){let e=async i=>await(0,rle.requestUrl)({url:VQo,method:"GET",headers:{...this.buildCopilotHeaders(i),Accept:"application/json","Openai-Intent":"model-access"},throw:!1}),r=await this.getValidCopilotToken(),n=await e(r);if(n.status===401&&(this.clearCopilotToken(),r=await this.getValidCopilotToken(),n=await e(r)),n.status!==200)throw new Error(`Failed to list models: ${n.status}`);let o=this.getRequestUrlJson(n);return this.modelPolicyTermsCache.clear(),o.data?.forEach(i=>{i.policy?.terms&&this.modelPolicyTermsCache.set(i.id,i.policy.terms)}),o}getPolicyTerms(e){return this.modelPolicyTermsCache.get(e)}clearCopilotToken(){A_({githubCopilotToken:"",githubCopilotTokenExpiresAt:0})}buildCopilotRequestHeaders(e){return this.buildCopilotHeaders(e)}invalidateCopilotToken(){this.clearCopilotToken()}getRequestUrlJson(e){if(typeof e.json=="string")try{return JSON.parse(e.json)}catch{return e.json}return e.json}parseCopilotTokenExpiresAt(e){if(!e||typeof e!="object")return Date.now()+xzr;let r=e,n=this.parseExpiresAtValue(r.expires_at);if(n!==null)return n;let o=r.expires_in,i=typeof o=="number"?o:typeof o=="string"?Number(o):Number.NaN;return Number.isFinite(i)&&i>0?Date.now()+i*1e3:Date.now()+xzr}parseExpiresAtValue(e){if(typeof e=="number")return!Number.isFinite(e)||e<=0?null:e>1e12?e:e*1e3;if(typeof e=="string"){let r=e.trim();if(!r)return null;let n=Number(r);if(Number.isFinite(n)&&n>0)return n>1e12?n:n*1e3;let o=Date.parse(r);if(!Number.isNaN(o)&&o>0)return o}return null}delay(e,r){return r?r.aborted?Promise.reject(new B0):new Promise((n,o)=>{let i=()=>{clearTimeout(a),o(new B0)},a=setTimeout(()=>{r.removeEventListener("abort",i),n()},e);r.addEventListener("abort",i,{once:!0})}):new Promise(n=>setTimeout(n,e))}}});function KQo(t){return typeof t=="string"?t:t==null?"":Array.isArray(t)?t.map(e=>typeof e=="string"?e:e&&typeof e=="object"&&typeof e.text=="string"?e.text:"").join(""):typeof t=="object"&&typeof t.text=="string"?t.text:""}var JQo,oDe,Tzr=v(()=>{CD();nDe();Dt();JQo=4;oDe=class t extends iA{constructor(r){let{fetchImplementation:n,configuration:o,apiKey:i,...a}=r,s=g$.getInstance(),u=n??o?.fetch??fetch,l=t.buildAuthedFetch(s,u);super({...a,apiKey:i||"copilot-dynamic-token",streamUsage:!1,configuration:{...o??{},baseURL:o?.baseURL??omt,fetch:l}});this.lc_serializable=!1;this.lc_namespace=["langchain","chat_models","github_copilot"]}static buildAuthedFetch(r,n){return async(o,i={})=>{let a=typeof o=="string"?o:typeof Request<"u"&&o instanceof Request?o.url:o.toString(),s=async c=>{let d=r.buildCopilotRequestHeaders(c),f=new Headers(i.headers);for(let[p,m]of Object.entries(d))f.set(p,m);return n(a,{...i,headers:f})},u=await r.getValidCopilotToken(),l=await s(u);if(l.status===401){try{await l.body?.cancel()}catch{}r.invalidateCopilotToken(),u=await r.getValidCopilotToken(),l=await s(u)}return l}}_llmType(){return"github-copilot"}_convertCompletionsDeltaToBaseMessageChunk(r,n,o){return!r.role&&!o&&(o="assistant"),r.content=KQo(r.content),super._convertCompletionsDeltaToBaseMessageChunk(r,n,o)}async getNumTokens(r){let n=tF(r);return n?Math.ceil(n.length/JQo):0}}});var imt={};En(imt,{default:()=>U0,normalizeAzureUrl:()=>Ezr});function Ezr(t){if(!t)return{baseUrl:void 0,apiVersion:void 0};let e;try{e=new URL(t)}catch{return{baseUrl:t,apiVersion:void 0}}let r=e.searchParams.get("api-version")||void 0;e.search="";let n=e.toString().replace(/\/+$/,"");return n=n.replace(/\/(chat\/completions|embeddings)$/,""),{baseUrl:n,apiVersion:r}}var Ozr,GQo,pd,U0,LW=v(()=>{na();gr();BC();Xe();id();Qe();Dt();uie();Hvr();att();rv();Jvr();btt();wwr();oUr();Dtt();CD();aUr();OS();Ozr=require("obsidian");vzr();wzr();Szr();Tzr();sV.prototype.getNumTokens=async t=>{let e=typeof t=="string"?t:t.map(r=>typeof r=="string"?r:r.text??"").join("");return Math.ceil(e.length/4)};GQo={openai:Px,"azure openai":Px,anthropic:tut,cohereai:ott,google:gtt,xai:Oft,openrouterai:fX,ollama:Att,"lm-studio":fX,groq:Rut,"3rd party (openai-format)":Px,siliconflow:Px,"copilot-plus":fX,mistralai:Tft,deepseek:rut,"amazon-bedrock":rDe,"github-copilot":oDe};pd=class pd{constructor(){this.providerApiKeyMap={openai:()=>ee().openAIApiKey,google:()=>ee().googleApiKey,"azure openai":()=>ee().azureOpenAIApiKey,anthropic:()=>ee().anthropicApiKey,cohereai:()=>ee().cohereApiKey,openrouterai:()=>ee().openRouterAiApiKey,groq:()=>ee().groqApiKey,xai:()=>ee().xaiApiKey,ollama:()=>"default-key","lm-studio":()=>"default-key","3rd party (openai-format)":()=>"default-key","copilot-plus":()=>ee().plusLicenseKey,mistralai:()=>ee().mistralApiKey,deepseek:()=>ee().deepseekApiKey,"amazon-bedrock":()=>ee().amazonBedrockApiKey,siliconflow:()=>ee().siliconflowApiKey,"github-copilot":()=>ee().githubCopilotToken||ee().githubCopilotAccessToken};this.buildModelMap(),ml(()=>{this.buildModelMap(),this.validateCurrentModel()})}static getInstance(){return pd.instance||(pd.instance=new pd),pd.instance}getTemperatureForModel(e,r,n){if(!e.isThinkingEnabled)return e.isOSeries||e.isGPT5?pd.REASONING_MODEL_TEMPERATURE:r.temperature??n.temperature}async getModelConfig(e){let r=ee(),n=e.name,o=kJ(n),{isThinkingEnabled:i}=o,a=this.getTemperatureForModel(o,e,r),s=e.maxTokens??r.maxTokens,u={modelName:n,streaming:e.stream??!0,maxRetries:3,maxConcurrency:3,enableCors:e.enableCors,...!i&&a!==void 0?{temperature:a}:{}},c={openai:{modelName:n,apiKey:await kn(e.apiKey||r.openAIApiKey),configuration:{baseURL:e.baseUrl,fetch:e.enableCors?hl:void 0,organization:await kn(e.openAIOrgId||r.openAIOrgId)},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)},anthropic:{anthropicApiKey:await kn(e.apiKey||r.anthropicApiKey),model:n,anthropicApiUrl:e.baseUrl,clientOptions:{defaultHeaders:{"anthropic-dangerous-direct-browser-access":"true"},fetch:e.enableCors?hl:void 0},...i&&{thinking:{type:"enabled",budget_tokens:pd.ANTHROPIC_THINKING_BUDGET_TOKENS}}},"azure openai":await(async()=>{let m=Ezr(e.baseUrl);return{modelName:e.baseUrl?n:e.azureOpenAIApiDeploymentName||r.azureOpenAIApiDeploymentName,apiKey:await kn(e.apiKey||r.azureOpenAIApiKey),configuration:{baseURL:m.baseUrl||`https://${e.azureOpenAIApiInstanceName||r.azureOpenAIApiInstanceName}.openai.azure.com/openai/deployments/${e.azureOpenAIApiDeploymentName||r.azureOpenAIApiDeploymentName}`,defaultQuery:{"api-version":m.apiVersion||e.azureOpenAIApiVersion||r.azureOpenAIApiVersion||"2024-05-01-preview"},defaultHeaders:{"Content-Type":"application/json","api-key":await kn(e.apiKey||r.azureOpenAIApiKey)},fetch:e.enableCors?hl:void 0},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)}})(),cohereai:{apiKey:await kn(e.apiKey||r.cohereApiKey),model:n},google:{apiKey:await kn(e.apiKey||r.googleApiKey),model:n,safetySettings:[{category:dV.HARM_CATEGORY_SEXUALLY_EXPLICIT,threshold:fV.BLOCK_NONE},{category:dV.HARM_CATEGORY_HATE_SPEECH,threshold:fV.BLOCK_NONE},{category:dV.HARM_CATEGORY_DANGEROUS_CONTENT,threshold:fV.BLOCK_NONE},{category:dV.HARM_CATEGORY_HARASSMENT,threshold:fV.BLOCK_NONE}],baseUrl:e.baseUrl},xai:{apiKey:await kn(e.apiKey||r.xaiApiKey),model:n},openrouterai:{modelName:n,apiKey:await kn(e.apiKey||r.openRouterAiApiKey),configuration:{baseURL:e.baseUrl||"https://openrouter.ai/api/v1",fetch:e.enableCors?hl:void 0,defaultHeaders:{"HTTP-Referer":"https://obsidiancopilot.com","X-Title":"Obsidian Copilot"}},enableReasoning:e.capabilities?.includes("reasoning")??!1,reasoningEffort:e.capabilities?.includes("reasoning")&&e.reasoningEffort?e.reasoningEffort:void 0,enablePromptCaching:e.enablePromptCaching??!0},groq:{apiKey:await kn(e.apiKey||r.groqApiKey),model:n},ollama:{model:n,baseUrl:e.baseUrl||"http://localhost:11434",headers:{Authorization:`Bearer ${await kn(e.apiKey||"default-key")}`},think:e.capabilities?.includes("reasoning")??!1,repeatPenalty:1.1,numCtx:e.numCtx??UJ},"lm-studio":{modelName:n,apiKey:e.apiKey||"default-key",streamUsage:e.streamUsage??!1,configuration:{baseURL:e.baseUrl||"http://localhost:1234/v1",fetch:e.enableCors?hl:void 0},enableReasoning:e.capabilities?.includes("reasoning")??!1,reasoningEffort:e.capabilities?.includes("reasoning")&&e.reasoningEffort?e.reasoningEffort:void 0},"3rd party (openai-format)":{modelName:n,apiKey:await kn(e.apiKey||r.openAIApiKey),streamUsage:e.streamUsage??!1,configuration:{baseURL:e.baseUrl,fetch:e.enableCors?hl:void 0,defaultHeaders:{"dangerously-allow-browser":"true"}},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)},siliconflow:{modelName:n,apiKey:await kn(e.apiKey||r.siliconflowApiKey),configuration:{baseURL:e.baseUrl||qC.siliconflow.host,fetch:e.enableCors?hl:void 0},...this.getOpenAISpecialConfig(n,e.maxTokens??r.maxTokens,e.temperature??r.temperature,e)},"copilot-plus":{modelName:n,apiKey:await kn(r.plusLicenseKey),configuration:{baseURL:i1,fetch:hl}},mistralai:{model:n,apiKey:await kn(e.apiKey||r.mistralApiKey),serverURL:e.baseUrl},deepseek:{modelName:n,apiKey:await kn(e.apiKey||r.deepseekApiKey),configuration:{baseURL:e.baseUrl||qC.deepseek.host,fetch:e.enableCors?hl:void 0}},"amazon-bedrock":{},"github-copilot":{modelName:n,fetchImplementation:e.enableCors?Rge:void 0}}[e.provider]||{};e.provider==="amazon-bedrock"&&(c=await this.buildBedrockConfig(e,n,r,s,a));let d=this.getProviderSpecificParams(e.provider,e);return{...u,...c,...d,...{maxTokens:s}}}getOpenAISpecialConfig(e,r,n,o){let i=ee(),a=kJ(e),s=this.getTemperatureForModel(a,o||{},i),u={maxTokens:r,temperature:s};if((a.isOSeries||a.isGPT5)&&o?.reasoningEffort&&(u.reasoning={effort:o.reasoningEffort},a.isGPT5&&o?.verbosity&&o?.provider!=="azure openai")){let l=o.verbosity;u.text={verbosity:l}}return u}async buildBedrockConfig(e,r,n,o,i){let a=e.apiKey||n.amazonBedrockApiKey;if(!a)throw new Error("Amazon Bedrock API key is not configured. Provide a key in Settings > API Keys or the model definition.");let s=await kn(a),u=e.bedrockRegion?.trim(),l=n.amazonBedrockRegion?.trim(),c=u||l||"us-east-1",d=e.baseUrl?.trim(),p=(d?d.replace(/\/+$/,""):void 0)||`https://bedrock-runtime.${c}.amazonaws.com`,m=encodeURIComponent(r),h=`${p}/model/${m}/invoke`,y=`${p}/model/${m}/invoke-with-response-stream`,g=e.enableCors?hl:void 0,_=/(^|\.)anthropic\./.test(r)?"bedrock-2023-05-31":void 0,w=e.capabilities?.includes("reasoning")??!1;return{modelName:r,modelId:r,apiKey:s,endpoint:h,streamEndpoint:y,defaultMaxTokens:o,defaultTemperature:i,defaultTopP:e.topP,anthropicVersion:_,enableThinking:w,fetchImplementation:g,streaming:e.stream??!0}}getProviderSpecificParams(e,r){let n={};return r.topP!==void 0&&["openai","azure openai","anthropic","google","openrouterai","ollama","lm-studio","3rd party (openai-format)","mistralai","deepseek","siliconflow"].includes(e)&&(n.topP=r.topP),r.frequencyPenalty!==void 0&&["openai","azure openai","openrouterai","ollama","lm-studio","3rd party (openai-format)","mistralai","deepseek","siliconflow"].includes(e)&&(n.frequencyPenalty=r.frequencyPenalty),n}buildModelMap(){let e=ee().activeModels;pd.modelMap={};let r=pd.modelMap;(e??aO).forEach(o=>{if(o.enabled){if(!Object.values(Mb).contains(o.provider)){console.warn(`Unknown provider: ${o.provider} for model: ${o.name}`);return}let i=this.getProviderConstructor(o),a=this.hasProviderCredentials(o),s=no(o);r[s]={hasApiKey:a,AIConstructor:i,vendor:o.provider}}})}hasProviderCredentials(e){if(e.provider==="amazon-bedrock"){let n=ee();return!!(e.apiKey||n.amazonBedrockApiKey)}let r=this.providerApiKeyMap[e.provider];return r?!!(e.apiKey||r()):!!e.apiKey}getProviderConstructor(e){let r=GQo[e.provider];if(!r)throw console.warn(`Unknown provider: ${e.provider} for model: ${e.name}`),new Error(`Unknown provider: ${e.provider} for model: ${e.name}`);return r}getChatModel(){if(!pd.chatModel)throw new Error("No valid chat model available. Please check your API key settings.");return pd.chatModel}isModelConfigValid(e,r){let n=no(e),o=pd.modelMap[n];return!(!o||!o.hasApiKey||e.plusExclusive&&!Dbr())}resolveModelForTemperatureOverride(){let e=ee();try{let r=j_();if(r){let n=PS(r,e.activeModels);if(this.isModelConfigValid(n,e))return n}}catch{}for(let r of e.activeModels)if(r.enabled&&!r.believerExclusive&&this.isModelConfigValid(r,e))return r;throw new Error("No valid chat model available for temperature override. Please check your API key settings and ensure at least one model is properly configured.")}async getChatModelWithTemperature(e){let n={...this.resolveModelForTemperatureOverride(),temperature:e};return await this.createModelInstance(n)}async setChatModel(e){try{let r=await this.createModelInstance(e);pd.chatModel=r,kJ(e.name).isGPT5&&(e.provider==="openai"||e.provider==="3rd party (openai-format)")&&F(`Chat model set with Responses API for GPT-5: ${e.name}`)}catch(r){throw ie(r),r}}async createModelInstance(e){let r=no(e),n=pd.modelMap[r];if(!n)throw new Error(`No model found for: ${r}`);if(!n.hasApiKey){let u=`API key is not provided for the model: ${r}.`;throw e.provider==="copilot-plus"?new y4("Copilot Plus license key is not configured. Please enter your license key in the Copilot Plus section at the top of Basic Settings."):new nte(u)}let o=await this.getModelConfig(e),i=kJ(e.name),a={...o};if(i.isGPT5&&(n.vendor==="openai"||n.vendor==="3rd party (openai-format)")&&(a.useResponsesApi=!0,F(`Enabling Responses API for GPT-5 model: ${e.name} (${n.vendor})`)),e.provider==="lm-studio"&&e.useResponsesApi!==!1){let u=new tle(a);return F(`[ChatModelManager] Using Responses API for LM Studio model: ${e.name}`),u}return new n.AIConstructor(a)}validateChatModel(e){return e!=null}estimateTokens(e){return e?Math.ceil(e.length/4):0}async countTokens(e){return pd.chatModel?.getNumTokens(e)??this.estimateTokens(e)}validateCurrentModel(){if(!pd.chatModel)return;let e=j_();if(!e)return;pd.modelMap[e]?.hasApiKey||(pd.chatModel=null,F("Failed to reinitialize model due to missing API key"))}async ping(e){let r=async n=>{let o={...e,enableCors:n},i=await this.getModelConfig(o),{streaming:a,maxTokens:s,maxCompletionTokens:u,...l}=i,c=kJ(e.name),f={maxTokens:c.isThinkingEnabled?4096:30},p={...l,...f};c.isGPT5&&(e.provider==="openai"||e.provider==="3rd party (openai-format)")&&(p.useResponsesApi=!0),await(e.provider==="lm-studio"&&e.useResponsesApi!==!1?new tle(p):new(this.getProviderConstructor(o))(p)).invoke([{role:"user",content:"hello"}],{timeout:8e3})};try{return await r(!1),!0}catch(n){console.log("First ping attempt failed, trying with CORS...");try{return await r(!0),new Ozr.Notice("Connection successful, but requires CORS to be enabled. Please enable CORS for this model once you add it above."),!0}catch(o){let i=` without CORS Error: `+Wr(n)+` with CORS Error: `+Wr(o);throw new Error(i)}}}findModelByName(e){return ee().activeModels.find(n=>n.name===e)}};pd.ANTHROPIC_THINKING_BUDGET_TOKENS=2048,pd.REASONING_MODEL_TEMPERATURE=1;U0=pd});var umt={};En(umt,{TieredLexicalRetriever:()=>Yx});async function XQo(){try{if(!amt){let e=await Promise.resolve().then(()=>(LW(),imt));amt=()=>e.default.getInstance()}return amt().getChatModel()}catch{return null}}var smt,amt,Yx,pX=v(()=>{Xe();Qe();Dt();OD();o9();smt=require("obsidian");oae();cH();amt=null;Yx=class extends fA{constructor(r,n){super();this.app=r;this.options=n;this.lc_namespace=["tiered_lexical_retriever"];this.lastQueryExpansion=null;this.searchCore=new OEe(r,XQo),this.chunkManager=lq(r)}getLastQueryExpansion(){return this.lastQueryExpansion}async getRelevantDocuments(r,n){try{let i=ES(r,this.app.vault).map(f=>f.basename),a=[...new Set([...this.options.salientTerms,...i])];ee().debug&&F("TieredLexicalRetriever: Starting search",{query:r,salientTerms:a,maxK:this.options.maxK});let s=ee(),u=await this.searchCore.retrieve(r,{maxResults:this.options.maxK,salientTerms:a,enableLexicalBoosts:s.enableLexicalBoosts,preExpandedQuery:this.options.preExpandedQuery}),l=u.results;this.lastQueryExpansion=u.queryExpansion;let c=await this.convertToDocuments(l),d=this.sortResults(c);return ee().debug&&F("TieredLexicalRetriever: Search complete",{totalResults:d.length,searchResults:l.length}),d}catch(o){return Ce("TieredLexicalRetriever: Error during search",o),[]}}async convertToDocuments(r){let n=[];for(let o of r)try{if(o.id.includes("#")){let[a]=o.id.split("#"),s=this.app.vault.getAbstractFileByPath(a);if(!s||!(s instanceof smt.TFile))continue;let u="",l=this.chunkManager;if(typeof l.getChunkText=="function"?u=await l.getChunkText(o.id):typeof l.getChunkTextSync=="function"&&(u=l.getChunkTextSync(o.id)||""),!u)continue;let c=this.app.metadataCache.getFileCache(s);n.push(new Yl({pageContent:u,metadata:{path:a,chunkId:o.id,title:s.basename,mtime:s.stat.mtime,ctime:s.stat.ctime,tags:c?.tags?.map(d=>d.tag)||[],score:o.score,rerank_score:o.score,engine:o.engine||"chunk-v3",includeInContext:o.score>(this.options.minSimilarityScore||.1),explanation:o.explanation,isChunk:!0}}))}else{let a=this.app.vault.getAbstractFileByPath(o.id);if(!a||!(a instanceof smt.TFile))continue;let s=await this.app.vault.cachedRead(a);if(!s)continue;let u=this.app.metadataCache.getFileCache(a);n.push(new Yl({pageContent:s,metadata:{path:o.id,title:a.basename,mtime:a.stat.mtime,ctime:a.stat.ctime,tags:u?.tags?.map(l=>l.tag)||[],score:o.score,rerank_score:o.score,engine:o.engine||"v3",includeInContext:o.score>(this.options.minSimilarityScore||.1),explanation:o.explanation,isChunk:!1}}))}}catch(i){Ce(`TieredLexicalRetriever: Failed to convert result ${o.id}`,i)}return F(`TieredLexicalRetriever: Converted ${n.length} results to Documents`),n}sortResults(r){return r.sort((n,o)=>{let i=n.metadata.score||0,s=(o.metadata.score||0)-i;if(Math.abs(s)>.01)return s;if(n.metadata.isChunk&&o.metadata.isChunk&&n.metadata.path===o.metadata.path){let u=parseInt(n.metadata.chunkId?.split("#")[1]||"0"),l=parseInt(o.metadata.chunkId?.split("#")[1]||"0");return u-l}return s})}}});var Pzr={};En(Pzr,{MergedSemanticRetriever:()=>FW});var RB,FW,lmt=v(()=>{PEe();cH();pX();OD();o9();RB=class RB extends fA{constructor(r,n,o){super();this.app=r;this.options=n;this.lc_namespace=["merged_semantic_retriever"];this.originalMaxK=Math.max(1,n.maxK),this.returnAll=!!n.returnAll;let i=this.returnAll?Pl:Math.min(this.originalMaxK*2,Pl),a={...n,maxK:i,returnAll:this.returnAll};this.lexicalRetriever=new Yx(r,a);let s=this.returnAll?Pl:Math.min(this.originalMaxK*2,Pl);this.semanticRetriever=o||new hae({minSimilarityScore:n.minSimilarityScore??.1,maxK:s,salientTerms:n.salientTerms,timeRange:n.timeRange,textWeight:n.textWeight,returnAll:this.returnAll,useRerankerThreshold:n.useRerankerThreshold})}async getRelevantDocuments(r){let[n,o]=await Promise.all([this.lexicalRetriever.getRelevantDocuments(r),this.semanticRetriever.getRelevantDocuments(r)]),i=new Map;for(let u of n)this.insertResult(i,u,"lexical");for(let u of o)this.insertResult(i,u,"semantic");let a=Array.from(i.values()).sort((u,l)=>(l.metadata?.score??0)-(u.metadata?.score??0)),s=this.returnAll?Pl:this.originalMaxK;return a.slice(0,s)}insertResult(r,n,o){let i=this.getDocumentKey(n),a=this.decorateDocument(n,o),s=r.get(i);if(!s){r.set(i,a);return}let u=s.metadata?.source,l=this.getDocumentScore(s),c=this.getDocumentScore(a);if(o==="lexical"){(u!=="lexical"||c>l)&&r.set(i,a);return}u!=="lexical"&&c>l&&r.set(i,a)}getDocumentKey(r){let n=r.metadata??{};return n.chunkId||n.path||n.id||n.title||`${r.pageContent.slice(0,64)}::${r.pageContent.length}`}decorateDocument(r,n){let o={...r.metadata??{},source:n},i=this.extractBaseScore(o),a=n==="lexical"?RB.LEXICAL_WEIGHT:RB.SEMANTIC_WEIGHT,s=i*a;return n==="lexical"&&this.hasTagMatch(o)&&(s*=RB.TAG_MATCH_BOOST),o.score=s,o.rerank_score=s,new Yl({pageContent:r.pageContent,metadata:o})}extractBaseScore(r){let n=[r?.rerank_score,r?.score];for(let o of n)if(typeof o=="number"&&!Number.isNaN(o))return o;return 0}getDocumentScore(r){let n=r.metadata?.score;return typeof n=="number"&&!Number.isNaN(n)?n:0}hasTagMatch(r){let n=r?.explanation;if(!n)return!1;let o=n.lexicalMatches;return Array.isArray(o)?o.some(i=>i?.field==="tags"):!1}};RB.LEXICAL_WEIGHT=1,RB.SEMANTIC_WEIGHT=1,RB.TAG_MATCH_BOOST=1.1;FW=RB});var Rzr={};En(Rzr,{FilterRetriever:()=>IB});var Mzr,IB,aDe=v(()=>{Xe();Qe();Wb();Dt();OD();Mzr=require("obsidian");cH();IB=class{constructor(e,r){this.app=e;this.options=r}async getRelevantDocuments(e){if(this.options.timeRange)return this.getTimeRangeDocuments(e);let r=ES(e,this.app.vault),n=await this.getTitleMatches(r),o=await this.getTagMatches(this.resolveTagTerms(e));return this.combineGuaranteedMatches(n,o)}hasTimeRange(){return this.options.timeRange!==void 0}async getTimeRangeDocuments(e){if(!this.options.timeRange)return[];let{startTime:r,endTime:n}=this.options.timeRange,o=this.generateDailyNoteDateRange(r,n);ee().debug&&F("FilterRetriever: Generated daily note titles",{startTime:new Date(r).toISOString(),endTime:new Date(n).toISOString(),titlesCount:o.length,firstTitle:o[0],lastTitle:o[o.length-1]});let{inclusions:i,exclusions:a}=Zl(),s=o.join(", "),u=ES(s,this.app.vault).filter(g=>fu(g,i,a)),c=(await this.getTitleMatches(u)).map(g=>(g.metadata.includeInContext=!0,g)),d=this.app.vault.getMarkdownFiles().filter(g=>fu(g,i,a)),f=[],p=this.options.returnAll?Pl:Math.min(this.options.maxK,Pl);for(let g of d)if(g.stat.mtime>=r&&g.stat.mtime<=n){if(u.some(b=>b.path===g.path))continue;if(f.length>=p)break;try{let b=await this.app.vault.cachedRead(g),_=this.app.metadataCache.getFileCache(g),w=(Date.now()-g.stat.mtime)/(1e3*60*60*24),T=Math.max(.3,Math.min(1,1-w/30));f.push(new Yl({pageContent:b,metadata:{path:g.path,title:g.basename,mtime:g.stat.mtime,ctime:g.stat.ctime,tags:_?.tags?.map(O=>O.tag)||[],includeInContext:!0,score:T,rerank_score:T,source:"time-filtered"}}))}catch(b){Ce(`FilterRetriever: Failed to read file ${g.path}`,b)}}let m=new Map;for(let g of c)m.set(g.metadata.path,g);for(let g of f)m.has(g.metadata.path)||m.set(g.metadata.path,{...g,metadata:{...g.metadata,includeInContext:!0}});let h=this.options.returnAll?Pl:this.options.maxK,y=Array.from(m.values()).sort((g,b)=>{let _=g.metadata.score||0;return(b.metadata.score||0)-_}).slice(0,h);return ee().debug&&F("FilterRetriever: Time range search complete",{timeRange:this.options.timeRange,dailyNotesFound:u.length,timeFilteredDocs:f.length,totalResults:y.length}),y}resolveTagTerms(e){let r=new Set;for(let n of this.options.salientTerms??[])typeof n=="string"&&n.startsWith("#")&&r.add(n.toLowerCase());if(r.size===0)for(let n of this.extractTagsFromQuery(e))r.add(n);return Array.from(r)}extractTagsFromQuery(e){if(!e)return[];let r=null;try{r=e.match(/#[\p{L}\p{N}_/-]+/gu)}catch{r=e.match(/#[a-z0-9_/-]+/g)}if(!r)return[];let n=new Set;for(let o of r){let i=o.trim();i.length<=1||n.add(i.toLowerCase())}return Array.from(n)}generateDailyNoteDateRange(e,r){let n=[],o=new Date(e),i=new Date(r),a=365;Math.ceil((r-e)/(1e3*60*60*24))>a&&(Ce(`FilterRetriever: Date range exceeds ${a} days, limiting to recent ${a} days`),o.setTime(i.getTime()-a*24*60*60*1e3));let u=new Date(o);for(;u<=i;)n.push(`[[${u.toLocaleDateString("en-CA")}]]`),u.setDate(u.getDate()+1);return n}async getTitleMatches(e){let r=[];for(let n of e)if(!tae(n))try{let o=await this.app.vault.cachedRead(n),i=this.app.metadataCache.getFileCache(n);r.push(new Yl({pageContent:o,metadata:{path:n.path,title:n.basename,mtime:n.stat.mtime,ctime:n.stat.ctime,tags:i?.tags?.map(a=>a.tag)||[],includeInContext:!0,score:1,rerank_score:1,source:"title-match"}}))}catch(o){Ce(`FilterRetriever: Failed to read title-matched file ${n.path}`,o)}return r}async getTagMatches(e){if(e.length===0)return[];let{inclusions:r,exclusions:n}=Zl(),o=this.app.vault.getMarkdownFiles(),i=[],a=this.options.returnAll?Pl:this.options.maxK;for(let s of o){if(i.length>=a)break;if(!fu(s,r,n)||tae(s))continue;let u=this.app.metadataCache.getFileCache(s);if(!u)continue;let l=(0,Mzr.getAllTags)(u)??[];if(!(l.length===0||!l.some(d=>{let f=d.toLowerCase();return e.some(p=>f===p||f.startsWith(p+"/"))})))try{let d=await this.app.vault.cachedRead(s);i.push(new Yl({pageContent:d,metadata:{path:s.path,title:s.basename,mtime:s.stat.mtime,ctime:s.stat.ctime,tags:l,includeInContext:!0,score:1,rerank_score:1,source:"tag-match"}}))}catch(d){Ce(`FilterRetriever: Failed to read tag-matched file ${s.path}`,d)}}return i}combineGuaranteedMatches(...e){let r=new Set,n=[];for(let o of e)for(let i of o)r.has(i.metadata.path)||(r.add(i.metadata.path),n.push(i));return n}}});var Izr={};En(Izr,{mergeFilterAndSearchResults:()=>mX});function mX(t,e){let r=new Set;for(let o of t)o.metadata?.path&&r.add(o.metadata.path);let n=e.filter(o=>{let i=o.metadata?.path;return!i||!r.has(i)});return{filterResults:t,searchResults:n}}var sDe=v(()=>{});var T4r=S((Imt,Amt)=>{(function(t,e){typeof Imt=="object"&&typeof Amt<"u"?Amt.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self).dayjs_plugin_quarterOfYear=e()})(Imt,function(){"use strict";var t="month",e="quarter";return function(r,n){var o=n.prototype;o.quarter=function(s){return this.$utils().u(s)?Math.ceil((this.month()+1)/3):this.month(this.month()%3+3*(s-1))};var i=o.add;o.add=function(s,u){return s=Number(s),this.$utils().p(u)===e?this.add(3*s,t):i.bind(this)(s,u)};var a=o.startOf;o.startOf=function(s,u){var l=this.$utils(),c=!!l.u(u)||u;if(l.p(s)===e){var d=this.quarter()-1;return c?this.month(3*d).startOf(t).startOf("day"):this.month(3*d+2).endOf(t).endOf("day")}return a.bind(this)(s,u)}}})});var FB=S((Nmt,Dmt)=>{(function(t,e){typeof Nmt=="object"&&typeof Dmt<"u"?Dmt.exports=e():typeof define=="function"&&define.amd?define(e):(t=typeof globalThis<"u"?globalThis:t||self).dayjs=e()})(Nmt,function(){"use strict";var t=1e3,e=6e4,r=36e5,n="millisecond",o="second",i="minute",a="hour",s="day",u="week",l="month",c="quarter",d="year",f="date",p="Invalid Date",m=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,h=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,y={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(B){var k=["th","st","nd","rd"],q=B%100;return"["+B+(k[(q-20)%10]||k[q]||k[0])+"]"}},g=function(B,k,q){var Z=String(B);return!Z||Z.length>=k?B:""+Array(k+1-Z.length).join(q)+B},b={s:g,z:function(B){var k=-B.utcOffset(),q=Math.abs(k),Z=Math.floor(q/60),V=q%60;return(k<=0?"+":"-")+g(Z,2,"0")+":"+g(V,2,"0")},m:function B(k,q){if(k.date()1)return B(X[0])}else{var W=k.name;w[W]=k,V=W}return!Z&&V&&(_=V),V||!Z&&_},P=function(B,k){if(O(B))return B.clone();var q=typeof k=="object"?k:{};return q.date=B,q.args=arguments,new I(q)},R=b;R.l=E,R.i=O,R.w=function(B,k){return P(B,{locale:k.$L,utc:k.$u,x:k.$x,$offset:k.$offset})};var I=function(){function B(q){this.$L=E(q.locale,null,!0),this.parse(q),this.$x=this.$x||q.x||{},this[T]=!0}var k=B.prototype;return k.parse=function(q){this.$d=function(Z){var V=Z.date,Y=Z.utc;if(V===null)return new Date(NaN);if(R.u(V))return new Date;if(V instanceof Date)return new Date(V);if(typeof V=="string"&&!/Z$/i.test(V)){var X=V.match(m);if(X){var W=X[2]-1||0,re=(X[7]||"0").substring(0,3);return Y?new Date(Date.UTC(X[1],W,X[3]||1,X[4]||0,X[5]||0,X[6]||0,re)):new Date(X[1],W,X[3]||1,X[4]||0,X[5]||0,X[6]||0,re)}}return new Date(V)}(q),this.init()},k.init=function(){var q=this.$d;this.$y=q.getFullYear(),this.$M=q.getMonth(),this.$D=q.getDate(),this.$W=q.getDay(),this.$H=q.getHours(),this.$m=q.getMinutes(),this.$s=q.getSeconds(),this.$ms=q.getMilliseconds()},k.$utils=function(){return R},k.isValid=function(){return this.$d.toString()!==p},k.isSame=function(q,Z){var V=P(q);return this.startOf(Z)<=V&&V<=this.endOf(Z)},k.isAfter=function(q,Z){return P(q){});var Jht={};En(Jht,{SequentialChain:()=>Boi,SimpleSequentialChain:()=>Uoi});function Tce(t){return Array.from(t).map(e=>`"${e}"`).join(", ")}var Boi,Uoi,Kht=v(()=>{ZX();pHr();Boi=class mHr extends nb{constructor(r){super(r);x(this,"chains");x(this,"inputVariables");x(this,"outputVariables");x(this,"returnAll");if(this.chains=r.chains,this.inputVariables=r.inputVariables,this.outputVariables=r.outputVariables??[],this.outputVariables.length>0&&r.returnAll)throw new Error("Either specify variables to return using `outputVariables` or use `returnAll` param. Cannot apply both conditions at the same time.");this.returnAll=r.returnAll??!1,this._validateChains()}static lc_name(){return"SequentialChain"}get inputKeys(){return this.inputVariables}get outputKeys(){return this.outputVariables}_validateChains(){if(this.chains.length===0)throw new Error("Sequential chain must have at least one chain.");let r=this.memory?.memoryKeys??[],n=new Set(this.inputKeys),o=new Set(r),i=Wht(n,o);if(i.size>0)throw new Error(`The following keys: ${Tce(i)} are overlapping between memory and input keys of the chain variables. This can lead to unexpected behaviour. Please use input and memory keys that don't overlap.`);let a=fHr(n,o);for(let s of this.chains){let u=xce(new Set(s.inputKeys),a);if(s.memory&&(u=xce(u,new Set(s.memory.memoryKeys))),u.size>0)throw new Error(`Missing variables for chain "${s._chainType()}": ${Tce(u)}. Only got the following variables: ${Tce(a)}.`);let l=new Set(s.outputKeys),c=Wht(a,l);if(c.size>0)throw new Error(`The following output variables for chain "${s._chainType()}" are overlapping: ${Tce(c)}. This can lead to unexpected behaviour.`);for(let d of l)a.add(d)}if(this.outputVariables.length===0)if(this.returnAll){let s=xce(a,n);this.outputVariables=Array.from(s)}else this.outputVariables=this.chains[this.chains.length-1].outputKeys;else{let s=xce(new Set(this.outputVariables),new Set(a));if(s.size>0)throw new Error(`The following output variables were expected to be in the final chain output but were not found: ${Tce(s)}.`)}}async _call(r,n){let o={},i=r,a=0;for(let u of this.chains){a+=1,o=await u.call(i,n?.getChild(`step_${a}`));for(let l of Object.keys(o))i[l]=o[l]}let s={};for(let u of this.outputVariables)s[u]=i[u];return s}_chainType(){return"sequential_chain"}static async deserialize(r){let n=[],o=r.input_variables,i=r.output_variables,a=r.chains;for(let s of a){let u=await nb.deserialize(s);n.push(u)}return new mHr({chains:n,inputVariables:o,outputVariables:i})}serialize(){let r=[];for(let n of this.chains)r.push(n.serialize());return{_type:this._chainType(),input_variables:this.inputVariables,output_variables:this.outputVariables,chains:r}}},Uoi=class hHr extends nb{constructor(r){super(r);x(this,"chains");x(this,"inputKey","input");x(this,"outputKey","output");x(this,"trimOutputs");this.chains=r.chains,this.trimOutputs=r.trimOutputs??!1,this._validateChains()}static lc_name(){return"SimpleSequentialChain"}get inputKeys(){return[this.inputKey]}get outputKeys(){return[this.outputKey]}_validateChains(){for(let r of this.chains){if(r.inputKeys.filter(n=>!r.memory?.memoryKeys.includes(n)).length!==1)throw new Error(`Chains used in SimpleSequentialChain should all have one input, got ${r.inputKeys.length} for ${r._chainType()}.`);if(r.outputKeys.length!==1)throw new Error(`Chains used in SimpleSequentialChain should all have one output, got ${r.outputKeys.length} for ${r._chainType()}.`)}}async _call(r,n){let o=r[this.inputKey],i=0;for(let a of this.chains)i+=1,o=(await a.call({[a.inputKeys[0]]:o,signal:r.signal},n?.getChild(`step_${i}`)))[a.outputKeys[0]],this.trimOutputs&&(o=o.trim()),await n?.handleText(o);return{[this.outputKey]:o}}_chainType(){return"simple_sequential_chain"}static async deserialize(r){let n=[],o=r.chains;for(let i of o){let a=await nb.deserialize(i);n.push(a)}return new hHr({chains:n})}serialize(){let r=[];for(let n of this.chains)r.push(n.serialize());return{_type:this._chainType(),chains:r}}}});var dje={};En(dje,{MapReduceDocumentsChain:()=>yHr,RefineDocumentsChain:()=>_Hr,StuffDocumentsChain:()=>cje});var cje,yHr,_Hr,Oce=v(()=>{ZX();QW();TS();cje=class gHr extends nb{constructor(r){super(r);x(this,"llmChain");x(this,"inputKey","input_documents");x(this,"documentVariableName","context");this.llmChain=r.llmChain,this.documentVariableName=r.documentVariableName??this.documentVariableName,this.inputKey=r.inputKey??this.inputKey}static lc_name(){return"StuffDocumentsChain"}get inputKeys(){return[this.inputKey,...this.llmChain.inputKeys].filter(r=>r!==this.documentVariableName)}get outputKeys(){return this.llmChain.outputKeys}_prepInputs(r){if(!(this.inputKey in r))throw new Error(`Document key ${this.inputKey} not found.`);let{[this.inputKey]:n,...o}=r,a=n.map(({pageContent:s})=>s).join(` diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/SG์ปค๋ฎค๋‹ˆํ‹ฐ.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/SG์ปค๋ฎค๋‹ˆํ‹ฐ.md new file mode 100644 index 0000000..a47e071 --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/SG์ปค๋ฎค๋‹ˆํ‹ฐ.md @@ -0,0 +1,76 @@ +# SG์ปค๋ฎค๋‹ˆํ‹ฐ ์ž๊ธฐ์†Œ๊ฐœ์„œ + +## 1. ์ง€์›๋™๊ธฐ + +### ๋‘ ์–ธ์–ด๋กœ ์Œ“์€ ๊ฒฝํ—˜์ด ๋‹ฟ๋Š” ์ง€์  + +์ €๋Š” ๊ท€์‚ฌ์˜ ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ MES ์ „ํ™˜ ๋ฐฉํ–ฅ๊ณผ Java/.NET ์ด์ค‘ ๊ธฐ์ˆ  ์Šคํƒ ์šด์˜ ๋ฐฉ์‹์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” ํ˜„๋Œ€ยท๊ธฐ์•„์ฐจ ๊ทธ๋ฃน์‚ฌ์˜ ์ƒ์‚ฐ์ •๋ณด์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ, AWS์™€ ASP.NET Core, Java๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด MES๋ฅผ ํด๋ผ์šฐ๋“œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ตฌ์กฐ๋กœ ํ˜„๋Œ€ํ™”ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ํŒŒ์•…ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์ œ์กฐํ˜„์žฅ์˜ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘๊ณผ ERPยทSCM ์—ฐ๊ณ„๊ฐ€ ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ์„ ์ขŒ์šฐํ•œ๋‹ค๋Š” ์ ์ด ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. + +์ €๋Š” ๊ทธ๋™์•ˆ Java/Spring Boot ๊ธฐ๋ฐ˜ ํ˜‘์—… ํ”Œ๋žซํผ(Didit)๊ณผ .NET ๊ธฐ๋ฐ˜ ์…€ํ”„ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์„œ๋น„์Šค(CloudSharp)๋ฅผ ์–‘์ชฝ ๋ชจ๋‘ ์„ค๊ณ„ํ•˜๋ฉฐ, ๋‘ ์ƒํƒœ๊ณ„๋ฅผ ์˜ค๊ฐ€๋Š” ๊ฒฝํ—˜์„ ์Œ“์•„์™”์Šต๋‹ˆ๋‹ค. Didit์—์„œ๋Š” Redis Pub/Sub๊ณผ SSE๋กœ AI Worker์˜ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ํŠน์ • ์‚ฌ์šฉ์ž์—๊ฒŒ ์‹ค์‹œ๊ฐ„ ์ „๋‹ฌํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ–ˆ๊ณ , CloudSharp์—์„œ๋Š” ASP.NET Core ๊ธฐ๋ฐ˜ Clean Architecture์™€ Docker Compose 5-์„œ๋น„์Šค ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์„ ์ง์ ‘ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ์–‘๋ฉด ๊ฒฝํ—˜์€ ๊ท€์‚ฌ๊ฐ€ ์ถ”๊ตฌํ•˜๋Š” ํด๋ผ์šฐ๋“œ MES์˜ ์‹ค์‹œ๊ฐ„์„ฑ๊ณผ ์‹ ๋ขฐ์„ฑ ์š”๊ตฌ์— ๊ทธ๋Œ€๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ๋‘ ์–ธ์–ด ํ™˜๊ฒฝ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์˜ค๊ฐ€๋ฉฐ ์ œ์กฐํ˜„์žฅ ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ์— ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์–ด ์ง€์›์„ ๊ฒฐ์‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๋‹ค + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์˜ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +CloudSharp ํ”„๋กœ์ ํŠธ์—์„œ tus ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ์˜ Finalize ๋‹จ๊ณ„์— ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ hook ์ฝœ๋ฐฑ๊ณผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ Worker๊ฐ€ ๋™์ผ ์—…๋กœ๋“œ ์„ธ์…˜์„ ๋‘ ๋ฒˆ ์ฒ˜๋ฆฌํ•ด, ๊ฐ™์€ ํŒŒ์ผ์ด ์ค‘๋ณต ์ƒ์„ฑ๋  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋ถ„์‚ฐ ๋ฝ์„ ๋„์ž…ํ•˜๋ฉด ZooKeeper๋‚˜ Redis Redlock ๊ฐ™์€ ์ถ”๊ฐ€ ์ธํ”„๋ผ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ €๋Š” ์ถ”๊ฐ€ ์ธํ”„๋ผ ์—†์ด ํ•ด๊ฒฐํ•  ๋ฐฉ๋ฒ•์„ ์ฐพ๊ธฐ ์œ„ํ•ด UploadSession์˜ ์ƒํƒœ ๋จธ์‹ ์„ ๋ฝ์œผ๋กœ ์žฌํ™œ์šฉํ•˜๋Š” CAS(Compare-And-Swap) ๋ฐฉ์‹์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. `UPDATE upload_session SET status='FINALIZING' WHERE status='UPLOADING'` ๋‹จ์ผ SQL๋กœ ์›์ž์  ์ ์œ ๋ฅผ ๊ตฌํ˜„ํ–ˆ๊ณ , ์˜ํ–ฅ ํ–‰ ์ˆ˜๊ฐ€ 1์ด๋ฉด ์ ์œ  ์„ฑ๊ณต, 0์ด๋ฉด ์ด๋ฏธ ์ฒ˜๋ฆฌ ์ค‘์œผ๋กœ ํŒ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํŒŒ์ผ ์ด๋™์ด๋ผ๋Š” ๋А๋ฆฐ I/O์™€ DB ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„๋ฅผ ๋ถ„๋ฆฌํ•ด ๋ฝ ์ง€์† ์‹œ๊ฐ„์„ ์ตœ์†Œํ™”ํ–ˆ๊ณ , 10๋ถ„ ์ด์ƒ FINALIZING ์ƒํƒœ์— ๋จธ๋ฌธ ์„ธ์…˜์„ ์ž๋™ ๋ณต๊ตฌํ•˜๋Š” Recovery Worker๋„ ํ•จ๊ป˜ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ ์ถ”๊ฐ€ ์ธํ”„๋ผ ์—†์ด ์ค‘๋ณต ์‹คํ–‰์„ 0๊ฑด์œผ๋กœ ์ฐจ๋‹จํ•˜๋ฉด์„œ, ์žฅ์•  ๋ฐœ์ƒ ์‹œ ์ž๋™ ๋ณต๊ตฌ๋˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ MES์ฒ˜๋Ÿผ ์‹ค์‹œ๊ฐ„์„ฑ๊ณผ ์ •ํ•ฉ์„ฑ์ด ํ•จ๊ป˜ ์š”๊ตฌ๋˜๋Š” ์‹œ์Šคํ…œ์—์„œ ์ด๋Ÿฌํ•œ ๋ถ„์„ยท์„ค๊ณ„ ๋ฐฉ์‹์œผ๋กœ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ•จ๊ป˜ ๊ณ ๋ฏผํ•˜๋‹ค + +์ž…์‚ฌ ํ›„์—๋Š” ๊ท€์‚ฌ์˜ ํด๋ผ์šฐ๋“œ ๊ธฐ๋ฐ˜ MES ์ „ํ™˜ ๋ฐฉํ–ฅ ์•ˆ์—์„œ, ์‹ค์‹œ๊ฐ„์„ฑ๊ณผ ์•ˆ์ •์„ฑ์„ ํ•จ๊ป˜ ์ฑ…์ž„์ง€๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ์—๋Š” ๊ท€์‚ฌ์˜ MES ๋„๋ฉ”์ธ๊ณผ ์ž๋™์ฐจ ์ œ์กฐ ๊ณต์ •์„ ๋น ๋ฅด๊ฒŒ ์ตํžˆ๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ Java/.NET ์–‘์ชฝ ๋ชจ๋“ˆ์˜ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ณ , AWS ์šด์˜ ๊ตฌ์กฐ์™€ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ํ•™์Šตํ•ด ์ž‘์€ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ๋‹จ๋…์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜์ค€์— ๋„๋‹ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +1๋…„ ๋‚ด์—๋Š” MES ๋ชจ๋“ˆ ๋‹จ์œ„ ๊ธฐ๋Šฅ์„ ์ฑ…์ž„์ง€๊ณ  ๊ฐœ๋ฐœํ•˜๋ฉฐ, ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘๊ณผ ERP ์—ฐ๋™ ๊ตฌ๊ฐ„์˜ ํ’ˆ์งˆ ๊ฐœ์„ ์— ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ทธ๋™์•ˆ Result ํŒจํ„ด๊ณผ ์ •ํ•ฉ์„ฑ ๊ฒ€์ฆ์œผ๋กœ ์Œ“์€ ์•ˆ์ •์  ์ฝ”๋“œ ์ž‘์„ฑ ์Šต๊ด€์„ ์ ์šฉํ•ด ์šด์˜ ์žฅ์• ๋ฅผ ์ค„์ด๋Š” ๋ฐ ๋ณดํƒฌ์ด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +3๋…„ ๋‚ด์—๋Š” ํด๋ผ์šฐ๋“œ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ตฌ์กฐ ๊ฐœ์„ ์„ ์ œ์•ˆํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ Docker Compose ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐ ์„œ๋น„์Šค ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜๊ณผ Healthcheck/Recovery ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•œ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ, MES์˜ ๋ชจ๋†€๋ฆฌ์‹-๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ „ํ™˜ ๊ณผ์ •์—์„œ ๊ตฌ์กฐ์  ์˜์‚ฌ๊ฒฐ์ •์— ์ฐธ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๋Œ€๊ทœ๋ชจ ์ œ์กฐ ํ˜„์žฅ ์šด์˜ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์—์„œ ๋ถ„์‚ฐ ํ™˜๊ฒฝ์˜ ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ์™€ ์žฅ์•  ๋ณต๊ตฌ๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•ด๋ณธ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๋ณด์™„ํ•ด๋‚˜๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • + +### ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ๊ฒฌ๊ณ ํ•œ ์ฝ”๋“œ๋กœ + +์ €๋Š” ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด, ์‹คํŒจ์™€ ๋™์‹œ์„ฑ์„ ๋ฏธ๋ฆฌ ๊ฐ€์ •ํ•˜๊ณ  ์„ค๊ณ„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ํ™”๋ฉด์— ๊ฒฐ๊ณผ๊ฐ€ ๋ณด์ด๋ฉด ๋งŒ์กฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Didit ํ”„๋กœ์ ํŠธ์—์„œ AI Worker๊ฐ€ ์žฅ์‹œ๊ฐ„ ์ถ”๋ก ํ•˜๋Š” ๋™์•ˆ API ์‘๋‹ต์ด ๋ฌถ์ด๋Š” ๋ฌธ์ œ์™€, ๊ฒฐ๊ณผ๊ฐ€ ๋ชจ๋“  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „ํŒŒ๋˜๋Š” ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผํ•˜๋ฉด์„œ ์ƒ๊ฐ์ด ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœ ๊ตฌํ˜„์œผ๋กœ๋Š” ํ•ด๊ฒฐ๋˜์ง€ ์•Š๊ณ , Redis Pub/Sub ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ํ์™€ ์‚ฌ์šฉ์ž๋ณ„ ํด๋ผ์ด์–ธํŠธ ํ‚ค ๋งคํ•‘์ด๋ผ๋Š” ๊ตฌ์กฐ ์ž์ฒด๋ฅผ ๋‹ค์‹œ ์„ค๊ณ„ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜ ์ดํ›„ ์ €๋Š” ์–ด๋–ค ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์—, ์–ด๋–ค ์‹คํŒจ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ถ€ํ„ฐ ์ •๋ฆฌํ•˜๋Š” ์Šต๊ด€์„ ๊ฐ–๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ๋Š” ์—…๋กœ๋“œ ์ค‘๋ณต, Quota ๊ฒฝํ•ฉ, ํŒŒ์ผ๋ช… ์ถฉ๋Œ ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‚ฌ์ „์— ๋„์ถœํ•˜๊ณ  CAS, Row-level Lock, ๋ช…์‹œ์  ์‹คํŒจ ๋ฐ˜ํ™˜์ด๋ผ๋Š” ์ •์ฑ…์œผ๋กœ ๋ถ„๋ฆฌํ•ด ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ˆ ํ†ต์—ฌ์ง€๋„์—์„œ๋Š” GPT์˜ hallucination์„ ๋ฐฉ์–ดํ•˜๊ธฐ ์œ„ํ•ด ํ—ˆ์šฉ ID Set ๊ฒ€์ฆ๊ณผ ๊ฑฐ๋ฆฌ์ˆœ ํด๋ฐฑ์„ ๋‹ค์ธต์œผ๋กœ ๋ฐฐ์น˜ํ•ด ์ž˜๋ชป๋œ ์ถ”์ฒœ์„ 0๊ฑด์œผ๋กœ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ๊ฒฝํ—˜์ด ์Œ“์ด๋ฉฐ ์ €๋Š” ์‹œ์Šคํ…œ์˜ ์ •ํ•ฉ์„ฑ๊ณผ ๋ณต๊ตฌ ๊ฐ€๋Šฅ์„ฑ์„ ํ•จ๊ป˜ ๊ณ ๋ฏผํ•˜๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•ด์™”์Šต๋‹ˆ๋‹ค. ์ œ์กฐํ˜„์žฅ์ฒ˜๋Ÿผ ์ž ๊น์˜ ์˜ค๋ฅ˜๋„ ์šด์˜์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ํ™˜๊ฒฝ์—์„œ, ์ด๋Ÿฌํ•œ ์‚ฌ๊ณ ๋ฐฉ์‹์œผ๋กœ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์„ ๋งŒ๋“œ๋Š” ๋ฐ ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +--- + +## ์ตœ์ข… ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [x] ํšŒ์‚ฌ ์—ฐ๊ฒฐ: SG์ปค๋ฎค๋‹ˆํ‹ฐ์˜ MES, AWS, Java/.NET ์ด์ค‘ ์Šคํƒ, ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์ „ํ™˜ ๋ฐฉํ–ฅ ๋ฐ˜์˜ +- [x] ๊ฒฝํ—˜ ์ฆ๋ช…: Didit, CloudSharp, ์ˆ ํ†ต์—ฌ์ง€๋„์˜ ๊ตฌ์ฒด์  ๊ธฐ์ˆ  ํŒ๋‹จ๊ณผ ํ–‰๋™ ๋ช…์‹œ +- [x] ์ˆ˜์น˜ ๊ฒฐ๊ณผ: "์ค‘๋ณต 0๊ฑด", "10๋ถ„ ์ด์ƒ FINALIZING ์ž๋™ ๋ณต๊ตฌ" ๋“ฑ ์ธก์ • ๊ฐ€๋Šฅํ•œ ํ‘œํ˜„ +- [x] ์ž…์‚ฌ ํ›„ ๊ธฐ์—ฌ: ์ดˆ๊ธฐ/1๋…„/3๋…„ ํƒ€์ž„๋ผ์ธ์œผ๋กœ ํšŒ์‚ฌ ๋ฐฉํ–ฅ๊ณผ ์„ฑ์žฅ ๊ณ„ํš ์—ฐ๊ฒฐ +- [x] ๊ธˆ์ง€ ํ‘œํ˜„ ์ œ๊ฑฐ: "์—ด์ •", "์ตœ์„ ", "์ธ์žฌ", "๋ˆ„๊ตฌ๋ณด๋‹ค" ๋“ฑ ๋ฏธ์‚ฌ์šฉ +- [x] ๋‘๊ด„์‹: ๋ชจ๋“  ๋ฌธํ•ญ ์ฒซ ๋ฌธ์žฅ์—์„œ ๊ฒฐ๋ก  ๋…ธ์ถœ +- [x] ์†Œ์ œ๋ชฉ: 15์ž ๋‚ด์™ธ, ์ถ”์ƒ์ ์ด์ง€ ์•Š์Œ + +--- + +**๋ณด์™„ ๊ฐ€๋Šฅ ํฌ์ธํŠธ** (ํ•„์š” ์‹œ ์•Œ๋ ค์ฃผ์„ธ์š”) +1. ์ž์†Œ์„œ ๊ธ€์ž ์ˆ˜ ์ œํ•œ์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ โ†’ 500์ž / 1000์ž ๋ฒ„์ „์œผ๋กœ ์žฌ๊ตฌ์„ฑ +2. SG์ปค๋ฎค๋‹ˆํ‹ฐ ์ฑ„์šฉ๊ณต๊ณ ์— ๋ณ„๋„ ๋ฌธํ•ญ(์˜ˆ: ํ˜‘์—… ๊ฒฝํ—˜, ๊ฐˆ๋“ฑ ํ•ด๊ฒฐ)์ด ์žˆ๋Š” ๊ฒฝ์šฐ โ†’ ์ถ”๊ฐ€ ๋ฌธํ•ญ ์ž‘์„ฑ +3. Java ๋˜๋Š” .NET ํ•œ์ชฝ์œผ๋กœ ๋น„์ค‘ ๊ฐ•์กฐ ํ•„์š” ์‹œ โ†’ ๊ธฐ์ˆ  ๋งค์นญ ์žฌ์กฐ์ • +4. ์‹ ์ž…/๊ฒฝ๋ ฅ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๋ณด์™„์  ํ†ค ์กฐ์ • \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/๋งˆ์ค€์†Œํ”„ํŠธ.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/๋งˆ์ค€์†Œํ”„ํŠธ.md new file mode 100644 index 0000000..dc4a04a --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/๋งˆ์ค€์†Œํ”„ํŠธ.md @@ -0,0 +1,65 @@ +# ๋งˆ์ค€์†Œํ”„ํŠธใˆœ ์ž๊ธฐ์†Œ๊ฐœ์„œ + +## 1. ์ง€์›๋™๊ธฐ + +### ์ธํ”„๋ผ ์šด์˜๊ณผ ์ž๋™ํ™”์˜ ๊ต์ฐจ์  + +์ €๋Š” ๋งˆ์ค€์†Œํ”„ํŠธ์˜ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ธํ”„๋ผ ์šด์˜ ๋ฐฉํ–ฅ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +SK-IDC์™€ KT-IDC๋ฅผ ์ง์ ‘ ์šด์˜ํ•˜๋ฉด์„œ๋„ NHNํด๋ผ์šฐ๋“œ์™€ ํ˜‘๋ ฅํ•ด ์˜์—ญ์„ ๋„“ํžˆ๋Š” ํ๋ฆ„์ด ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— RCS์™€ AI ์—ฐ์‚ฐ ์„œ๋ฒ„๋กœ ์„œ๋น„์Šค๋ฅผ ํ™•์žฅํ•˜๋Š” ๋ชจ์Šต์€, ๋‹จ์ˆœํ•œ ์„œ๋ฒ„ ์ž„๋Œ€๋ฅผ ๋„˜์–ด ์šด์˜ ์ž๋™ํ™”์™€ ์ง€๋Šฅํ™”๋ฅผ ํ•จ๊ป˜ ๊ณ ๋ฏผํ•˜๋Š” ๋ฐฉํ–ฅ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ๋ฐฉํ–ฅ์„ฑ์€ ์…€ํ”„ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์„œ๋น„์Šค CloudSharp๋ฅผ ์„ค๊ณ„ํ•˜๋ฉฐ ๋งˆ์ฃผํ•œ ๋ฌธ์ œ์˜์‹๊ณผ ๋งž๋‹ฟ์•„ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š” PostgreSQL, Redis, tusd, ASP.NET Core, nginx ๋‹ค์„ฏ ๊ฐœ ์„œ๋น„์Šค๋ฅผ Docker Compose๋กœ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ํ–ˆ์Šต๋‹ˆ๋‹ค. internal ๋„คํŠธ์›Œํฌ๋กœ DB์™€ Redis๋ฅผ ๊ฒฉ๋ฆฌํ•ด ์™ธ๋ถ€ ์ง„์ž…์ ์„ nginx ํ•˜๋‚˜๋กœ ์ œํ•œํ–ˆ์Šต๋‹ˆ๋‹ค. healthcheck ์กฐ๊ฑด์œผ๋กœ ์„œ๋น„์Šค ๊ธฐ๋™ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜๊ณ , Multi-stage Dockerfile๋กœ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋„ ํ•จ๊ป˜ ์ค„์˜€์Šต๋‹ˆ๋‹ค. + +์ด ๊ณผ์ •์—์„œ ์ธํ”„๋ผ ์•ˆ์ •์„ฑ์€ ์ข‹์€ ์žฅ๋น„๊ฐ€ ์•„๋‹ˆ๋ผ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์šด์˜ ๊ตฌ์กฐ์—์„œ ๋‚˜์˜จ๋‹ค๋Š” ์ ์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. ์ž์ฒด IDC์™€ ํด๋ผ์šฐ๋“œ๋ฅผ ํ•จ๊ป˜ ์šด์˜ํ•˜๋ฉฐ ๋ณด์•ˆ ์ธ์ฆ์„ ๊พธ์ค€ํžˆ ๊ด€๋ฆฌํ•ด ์˜จ ๋งˆ์ค€์†Œํ”„ํŠธ๋ผ๋ฉด, ๊ทธ๋™์•ˆ ์Œ“์•„ ์˜จ ์ธํ”„๋ผ ์„ค๊ณ„ ๊ฒฝํ—˜์„ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋” ๊นŠ๊ฒŒ ๋ฐœ์ „์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•ด ์ง€์›์„ ๊ฒฐ์‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ์ฆ๋ช…ํ•˜๋Š” AI ์ตœ์ ํ™” + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ AI ์‹œ์Šคํ…œ์˜ ๋น„์šฉ๊ณผ ํ’ˆ์งˆ์„ ๋™์‹œ์— ๊ฐœ์„ ํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +์ˆ ํ†ต์—ฌ์ง€๋„(Sulmap) ํ”„๋กœ์ ํŠธ์—์„œ GPT-5.2 ๊ธฐ๋ฐ˜ ์ˆ ์ง‘ ์ถ”์ฒœ ์—”์ง„์„ ์„ค๊ณ„ํ•  ๋•Œ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ์— ๋ถ€๋”ชํ˜”์Šต๋‹ˆ๋‹ค. ๋ฐ˜๊ฒฝ ๋‚ด ํ›„๋ณด๊ฐ€ ์ตœ๋Œ€ 200๊ฐœ์— ์ด๋ฅด๋Ÿฌ ํ•œ ๋ฒˆ์— ์ „์†กํ•˜๋ฉด ์ž…๋ ฅ์ด 16,000ํ† ํฐ์„ ๋„˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ GPT๊ฐ€ ํ›„๋ณด์— ์—†๋Š” ์ˆ ์ง‘์„ ๋งŒ๋“ค์–ด ๋‚ด๋Š” hallucination๋„ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. + +๋จผ์ € ํ† ํฐ ๋น„์šฉ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด 2๋‹จ๊ณ„ Cascade Ranking ๊ตฌ์กฐ๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. 200๊ฐœ๋ฅผ 100๊ฐœ์”ฉ ๋ฐฐ์น˜๋กœ ๋‚˜๋ˆ  1๋‹จ๊ณ„์—์„œ๋Š” ID๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ˜•ํƒœ๋กœ top 5์”ฉ ์„ ๋ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. 2๋‹จ๊ณ„์—์„œ๋Š” ์ตœ๋Œ€ 40๊ฐœ๋งŒ ์ •๋ฐ€ ๋žญํ‚นํ•ด Top 10๊ณผ ์ถ”์ฒœ ์ด์œ ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ๋”ํ•ด JSON ๋Œ€์‹  ๋„๋ฉ”์ธ ํŠนํ™” Pipe-delimited Format์„ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ‚ค ์ด๋ฆ„๊ณผ ๋”ฐ์˜ดํ‘œ ๊ฐ™์€ ๊ตฌ์กฐ ๋ฌธ์ž๊ฐ€ ์ฐจ์ง€ํ•˜๋˜ ํ† ํฐ์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•œ ์„ ํƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +๋‹ค์Œ์œผ๋กœ hallucination ๋ฌธ์ œ๋Š” ํ”„๋กฌํ”„ํŠธ์™€ ์ฝ”๋“œ ์–‘์ชฝ์—์„œ ๋ฐฉ์–ดํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์—์„œ๋Š” ํ›„๋ณด ์™ธ ID ์ƒ์„ฑ์„ ๋ช…์‹œ์ ์œผ๋กœ ๊ธˆ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ๋Š” ์ •๊ทœ์‹์œผ๋กœ ์ž…๋ ฅ B ๋ผ์ธ์—์„œ ํ—ˆ์šฉ ID Set์„ ์ถ”์ถœํ•ด, GPT ์‘๋‹ต์„ ์ฆ‰์‹œ ๋Œ€์กฐํ•˜๊ณ  ํ•„ํ„ฐ๋งํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ถ€์กฑํ•œ ๊ฒฐ๊ณผ๋Š” ๊ฑฐ๋ฆฌ์ˆœ fallback์œผ๋กœ ์ฑ„์›Œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๋„ ๋ณด์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ ํ˜ธ์ถœ๋‹น ํ† ํฐ์„ 1๋‹จ๊ณ„์—์„œ ์•ฝ 70%, ํฌ๋งท ๋ณ€๊ฒฝ์œผ๋กœ ์ถ”๊ฐ€ 40% ์ ˆ๊ฐํ–ˆ๊ณ  ์ž˜๋ชป๋œ ID ๋…ธ์ถœ์€ 0๊ฑด์œผ๋กœ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ ๋น„์šฉยทํ’ˆ์งˆยท์‹ ๋ขฐ์„ฑ์„ ์ˆ˜์น˜๋กœ ์ฆ๋ช…ํ•˜๋Š” ๋ฐฉ์‹์„ ์ตํ˜”์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ์ ‘๊ทผ์„ ๋งˆ์ค€์†Œํ”„ํŠธ์˜ AI ์—ฐ์‚ฐ ์„œ๋ฒ„ ์šด์˜๊ณผ ์„œ๋น„์Šค ์ง€๋Šฅํ™” ์—…๋ฌด์—๋„ ๊ทธ๋Œ€๋กœ ์ ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ์šด์˜ ๋ถ€๋‹ด์„ ์ฝ”๋“œ๋กœ ์ค„์ด๋Š” ๊ฐœ๋ฐœ์ž + +์ž…์‚ฌ ํ›„์—๋Š” IDC ์šด์˜๊ณผ ํด๋ผ์šฐ๋“œ ์ธํ”„๋ผ๋ฅผ ์ฝ”๋“œ๋กœ ์ž๋™ํ™”ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๋งˆ์ค€์†Œํ”„ํŠธ๋Š” ์ž์ฒด IDC๋ฅผ ์šด์˜ํ•˜๋ฉด์„œ๋„ NHNํด๋ผ์šฐ๋“œ ํŒŒํŠธ๋„ˆ์‹ญ๊ณผ RCS, AI ์—ฐ์‚ฐ ์„œ๋ฒ„๋กœ ์˜์—ญ์„ ๋„“ํžˆ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ฐ˜๋ณต ์šด์˜ ์ž‘์—…๊ณผ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์ฝ”๋“œ๋กœ ์˜ฎ๊ธฐ๋Š” ์—ญํ• ์ด ์ ์  ๋” ์ค‘์š”ํ•ด์ง„๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ 6๊ฐœ์›”์€ ASP/PHP ๊ธฐ๋ฐ˜ ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ IDC ์šด์˜ ํ”„๋กœ์„ธ์Šค, ๋ณด์•ˆ ์ •์ฑ…์„ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ์šด์˜ ํ๋ฆ„์„ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•œ ๋’ค์—์•ผ ์ž๋™ํ™” ๋Œ€์ƒ์ด ๋ณด์ธ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +1๋…„ ์ฐจ์—๋Š” ๋ฐ˜๋ณต์ ์ธ ์„œ๋ฒ„ ์ ๊ฒ€๊ณผ ๋กœ๊ทธ ์ˆ˜์ง‘์„ Python ์Šคํฌ๋ฆฝํŠธ์™€ ์•Œ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ์ „ํ™˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ Recovery Worker์™€ healthcheck ๊ธฐ๋ฐ˜ ๊ธฐ๋™ ์ˆœ์„œ๋ฅผ ์„ค๊ณ„ํ•ด ๋ณธ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝํ—˜์„ ์šด์˜ ์ž๋™ํ™” ์˜์—ญ์— ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒจ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ด…๋‹ˆ๋‹ค. + +3๋…„ ์ฐจ์—๋Š” AI ์—ฐ์‚ฐ ์„œ๋ฒ„์˜ ๋ถ€ํ•˜ ์˜ˆ์ธก๊ณผ ๋น„์ •์ƒ ํŠธ๋ž˜ํ”ฝ ํƒ์ง€, ๋ณด์•ˆ ์ ๊ฒ€ ์ž๋™ํ™”์ฒ˜๋Ÿผ ์ง€๋Šฅํ™” ์˜์—ญ์— ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. Sulmap์—์„œ GPT ํ˜ธ์ถœ์˜ ๋น„์šฉยทํ’ˆ์งˆ์„ ์ˆ˜์น˜๋กœ ์ธก์ •ํ•˜๊ณ  ๊ฐœ์„ ํ–ˆ๋˜ ๋ฐฉ์‹์œผ๋กœ, ์šด์˜ ์ง€ํ‘œ๋ฅผ ์ˆ˜์น˜๋กœ ์ •์˜ํ•˜๊ณ  ์ค„์—ฌ ๋‚˜๊ฐ€๋Š” ๋ฐ ๋ณดํƒฌ์ด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +ASP/PHP ์‹ค์„œ๋น„์Šค ์šด์˜ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ Java์™€ C# ๊ธฐ๋ฐ˜ ๋ฐฑ์—”๋“œ์™€ Linux ์ปจํ…Œ์ด๋„ˆ ์šด์˜ ๊ฒฝํ—˜์„ ํ† ๋Œ€๋กœ ํ•™์Šต ๊ธฐ๊ฐ„์„ ๊พธ์ค€ํžˆ ์ค„์—ฌ ๋‚˜๊ฐ€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • + +### ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ์œผ๋กœ + +์ €๋ฅผ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์†Œ๊ฐœํ•˜๋ฉด, ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ์œผ๋กœ ๊ด€์‹ฌ์ด ์˜ฎ๊ฒจ ๊ฐ„ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์…€ํ”„ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์„œ๋น„์Šค CloudSharp๋ฅผ ์„ค๊ณ„ํ•˜๋ฉฐ ๊ทธ ์ƒ๊ฐ์ด ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค. + +๋Œ€์šฉ๋Ÿ‰ ์—…๋กœ๋“œ๋ฅผ tusd๋กœ ์ฒ˜๋ฆฌํ•˜๋˜ ์ค‘ ๊ฐ™์€ ์—…๋กœ๋“œ๊ฐ€ ๋‘ ๋ฒˆ Finalize๋˜๋Š” ๊ฒฝ์Ÿ ์ƒํƒœ๊ฐ€ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ง‰๊ธฐ ์œ„ํ•ด ๋‹จ์ผ SQL ํ•œ ์ค„๋กœ ์ ์œ  ์—ฌ๋ถ€๋ฅผ ํŒ์ •ํ•˜๋Š” CAS ๋ฐฉ์‹์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. 10๋ถ„ ์ด์ƒ ๋ฉˆ์ถ˜ ์„ธ์…˜์€ Recovery Worker๊ฐ€ ์ž๋™ ๋ณต๊ตฌํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. Quota ๊ฒฝ์Ÿ ์กฐ๊ฑด๋„ PostgreSQL์˜ row-level lock์œผ๋กœ ํŒ์ •๊ณผ ์˜ˆ์•ฝ์„ ํ•œ ํŠธ๋žœ์žญ์…˜ ์•ˆ์—์„œ ์›์žํ™”ํ•ด ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ณผ์ •์—์„œ ์ข‹์€ ์‹œ์Šคํ…œ์€ ์žฅ์• ๊ฐ€ ์—†๋Š” ์‹œ์Šคํ…œ์ด ์•„๋‹˜์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. ์ข‹์€ ์‹œ์Šคํ…œ์€ ์žฅ์• ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ผ์–ด๋‚˜๊ณ  ์–ด๋–ป๊ฒŒ ๋ณต๊ตฌ๋˜๋Š”์ง€ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +์ดํ›„ TusBlazorClient๋ฅผ NuGet ํŒจํ‚ค์ง€๋กœ ๊ณต๊ฐœํ•˜๋ฉด์„œ ๊ฐ™์€ ์›์น™์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค๊ณ„์—๋„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. JS ๋ชจ๋“ˆ์€ Lazy ๋กœ๋”ฉ์œผ๋กœ ๋ฌถ๊ณ , IAsyncDisposable๋กœ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๋ช…ํ™•ํžˆ ๋…ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜๋Š” ์ชฝ์—์„œ ๋™์ž‘๊ณผ ๋น„์šฉ์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“  ์„ ํƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ๋งˆ์ค€์†Œํ”„ํŠธ์˜ IDC์™€ AI ์„œ๋ฒ„ ์šด์˜ ํ™˜๊ฒฝ์— ํ•ฉ๋ฅ˜ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์น˜๋กœ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์‹œ์Šคํ…œ์„, ๋งˆ์ค€์†Œํ”„ํŠธ์˜ ์šด์˜ ๋…ธํ•˜์šฐ ์œ„์—์„œ ํ•จ๊ป˜ ๋งŒ๋“ค์–ด ๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/์”จ์•ค์ง€ ๋งˆ์ดํฌ๋กœ์›จ์ด๋ธŒ.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์”จ์•ค์ง€ ๋งˆ์ดํฌ๋กœ์›จ์ด๋ธŒ.md new file mode 100644 index 0000000..6f4c811 --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์”จ์•ค์ง€ ๋งˆ์ดํฌ๋กœ์›จ์ด๋ธŒ.md @@ -0,0 +1,77 @@ +# ์”จ์•ค์ง€ ๋งˆ์ดํฌ๋กœ์›จ์ด๋ธŒ ์ž๊ธฐ์†Œ๊ฐœ์„œ + +> ์ž‘์„ฑ ๊ธฐ์ค€: IT ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ปจ๋ฒค์…˜, ์ง๋ฌด: S/W ๊ฐœ๋ฐœ(์œ„์„ฑ์ถ”์ ยท๋ชจ์…˜ ๋ฐ ๊ณ„์ธก๊ธฐ ์ œ์–ด / ์•ˆํ…Œ๋‚˜ ์ธก์ • ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ) + +--- + +## 1. ์ง€์›๋™๊ธฐ + +### ์ •๋ฐ€ ์ œ์–ด์™€ .NET ๊ฒฝํ—˜์ด ๋งŒ๋‚˜๋Š” ์ž๋ฆฌ + +๊ท€์‚ฌ๊ฐ€ ๊ตญ๋‚ด ์ตœ์ดˆ๋กœ ํ™•๋ณดํ•œ CATR ์ธก์ • ๊ธฐ์ˆ ๊ณผ .NET ๊ธฐ๋ฐ˜์˜ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ฑ„์šฉ๊ณต๊ณ ๋ฅผ ํ™•์ธํ•˜๋ฉฐ ์œ„์„ฑ์ถ”์ ยท๊ณ„์ธก๊ธฐ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ C#๊ณผ WPF๋กœ ๊ฐœ๋ฐœ๋œ๋‹ค๋Š” ์ ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. ์•ˆํ…Œ๋‚˜ ์ธก์ • ์‹œ์Šคํ…œ์€ ๋‹จ์ˆœ GUI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์•„๋‹ˆ๋ผ, ํฌ์ง€์…”๋„ˆ์™€ ๊ณ„์ธก๊ธฐ, ๋ฐ์ดํ„ฐ ๋ถ„์„์ด ํ•˜๋‚˜์˜ ํ๋ฆ„์œผ๋กœ ์—ฐ๊ฒฐ๋˜์–ด์•ผ ํ•˜๋Š” ์˜์—ญ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ™˜๊ฒฝ์—์„œ๋Š” ์™ธ๋ถ€ ํ•˜๋“œ์›จ์–ด์™€ ์•ˆ์ •์ ์œผ๋กœ ํ†ต์‹ ํ•˜๋Š” .NET ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„ ๋Šฅ๋ ฅ์ด ํ•ต์‹ฌ ์—ญ๋Ÿ‰์ด ๋œ๋‹ค๊ณ  ๋ณด์•˜์Šต๋‹ˆ๋‹ค. + +์ €๋Š” .NET ํ™˜๊ฒฝ์—์„œ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ†ต์‹ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•˜๊ณ  NuGet์— ๋ฐฐํฌํ•œ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. Blazor WebAssembly์—์„œ JavaScript tus ํ”„๋กœํ† ์ฝœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ C# API๋กœ ๋ž˜ํ•‘ํ•œ TusBlazorClient ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์ฝœ๋ฐฑ ๋ธŒ๋ฆฟ์ง€ ์„ค๊ณ„, ๋น„๋™๊ธฐ ๋ฆฌ์†Œ์Šค ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ, ์™ธ๋ถ€ ์‹œ์Šคํ…œ ํ˜ธ์ถœ ๋น„์šฉ ์ตœ์ ํ™”๋ฅผ ์ง์ ‘ ๋‹ค๋ค˜์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฌํ•œ ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์—ฐ๋™ ์„ค๊ณ„ ๊ฒฝํ—˜์€ ์•ˆํ…Œ๋‚˜ ์ธก์ • ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ๋‹ค์–‘ํ•œ ๊ณ„์ธก๊ธฐ์™€ ํฌ์ง€์…”๋„ˆ๋ฅผ ๋™์‹œ์— ์ œ์–ดํ•ด์•ผ ํ•˜๋Š” ํ™˜๊ฒฝ์—์„œ ๊ทธ๋Œ€๋กœ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ท€์‚ฌ๊ฐ€ ์ถ”๊ตฌํ•˜๋Š” ์‹ ๋ขฐ์„ฑ ์žˆ๋Š” ์ธก์ • ์‹œ์Šคํ…œ๊ณผ ์ œ๊ฐ€ ์Œ“์•„์˜จ .NET ์ธํ„ฐํŽ˜์ด์Šค ์„ค๊ณ„ ๊ฒฝํ—˜์ด ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๋Š” ์ ์ด ์ง€์›์„ ๊ฒฐ์‹ฌํ•œ ๊ฐ€์žฅ ํฐ ์ด์œ ์ž…๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์•ˆ์ •์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์„ค๊ณ„ + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ .NET ํ™˜๊ฒฝ์—์„œ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์•ˆ์ •์ ์œผ๋กœ ์—ฐ๊ฒฐ๋˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +Blazor WebAssembly์—์„œ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ TusBlazorClient๋ฅผ ๋‹จ๋…์œผ๋กœ ๊ฐœ๋ฐœํ•ด NuGet์— ๋ฐฐํฌํ•œ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. Blazor WASM์€ ์ˆœ์ˆ˜ C# I/O๋งŒ์œผ๋กœ ๋Œ€์šฉ๋Ÿ‰ ์ „์†ก์ด ์–ด๋ ค์›Œ, ๊ฒ€์ฆ๋œ JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ C# API๋กœ ๋ž˜ํ•‘ํ•˜๋Š” ๋ฐฉ์‹์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” .NET ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๊ฐ€ JSON ์ง๋ ฌํ™” ๋Œ€์ƒ์ด ์•„๋‹ˆ๋ผ๋Š” ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. JS์™€์˜ ์ฝœ๋ฐฑ ๋ธŒ๋ฆฟ์ง€๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ฝœ๋ฐฑ์„ ๋‹จ์ผ ๊ฐ์ฒด์— ๋ชจ์•„ `DotNetObjectReference`๋กœ ์ „๋‹ฌํ•˜๊ณ , ์˜ต์…˜ ์ง๋ ฌํ™” ์‹œ์—๋Š” `[JsonIgnore]`๋กœ ์ฝœ๋ฐฑ์„ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐ ์ฝœ๋ฐฑ์˜ null ์—ฌ๋ถ€๋ฅผ ๋ณ„๋„ ๊ฐ์ฒด๋กœ JS์— ์ „๋‹ฌํ•ด, ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝœ๋ฐฑ์€ `invokeMethodAsync` ํ˜ธ์ถœ ์ž์ฒด๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์ตœ์ ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. JS ๋ชจ๋“ˆ์€ `IAsyncDisposable`๊ณผ Lazy ์ดˆ๊ธฐํ™”๋กœ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•ด, ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€์—์„œ๋Š” ๋ชจ๋“ˆ์ด ์•„์˜ˆ ๋กœ๋“œ๋˜์ง€ ์•Š๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ 18๊ฐœ tus ์˜ต์…˜๊ณผ 7์ข… ์ฝœ๋ฐฑ์„ C#์— ์™„์ „ํžˆ ๋งคํ•‘ํ–ˆ๊ณ , Selenium ๊ธฐ๋ฐ˜ 9๊ฐœ E2E ํ…Œ์ŠคํŠธ๋กœ ์—…๋กœ๋“œ, ์žฌ๊ฐœ, ์˜ต์…˜ ๋™์  ๋ณ€๊ฒฝ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ NuGet์— v1.0.1๋กœ ๋ฐฐํฌ๋˜์–ด ์™ธ๋ถ€ ํ”„๋กœ์ ํŠธ์—์„œ ๊ทธ๋Œ€๋กœ ์„ค์น˜ํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์„ ํ†ตํ•ด ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ํ˜ธ์ถœ ๋น„์šฉ์„ ์ธก์ •ํ•˜๊ณ  ์ค„์ด๋Š” ์„ค๊ณ„ ๋ฐฉ์‹์„ ์ตํ˜”์Šต๋‹ˆ๋‹ค. ์•ˆํ…Œ๋‚˜ ์ธก์ • ์†Œํ”„ํŠธ์›จ์–ด ์—ญ์‹œ ๋‹ค์–‘ํ•œ ๊ณ„์ธก๊ธฐยทํฌ์ง€์…”๋„ˆ์™€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ†ต์‹ ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์•ˆ์ •์ ์ธ ์ œ์–ด ํ๋ฆ„๊ณผ ์ž์› ๊ด€๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๋ฐ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ์ธก์ • ์ •ํ™•๋„๋ฅผ ์ฝ”๋“œ๋กœ ์ง€ํ‚ค๋Š” ๊ฐœ๋ฐœ์ž + +์ž…์‚ฌ ํ›„์—๋Š” ๊ท€์‚ฌ์˜ ์ •๋ฐ€ ์ธก์ • ํ™˜๊ฒฝ ์•ˆ์—์„œ ์•ˆ์ •์„ฑ๊ณผ ์ •ํ™•๋„๋ฅผ ์ฑ…์ž„์ง€๋Š” .NET ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” CATR, Near-Field, Far-Field ๋“ฑ ๋‹ค์–‘ํ•œ ์ธก์ • ํ™˜๊ฒฝ์„ ์šด์šฉํ•˜๋ฉฐ, ๊ฐ ํ™˜๊ฒฝ์—์„œ ์ธก์ • ํŽธ์ฐจ๋ฅผ ์ตœ์†Œํ™”ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋„์ „์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ฝ”๋“œ ์ธก๋ฉด์—์„œ ๋’ท๋ฐ›์นจํ•˜๋ ค๋ฉด ๊ณ„์ธก๊ธฐ ํ†ต์‹  ํ”„๋กœํ† ์ฝœ, ํฌ์ง€์…”๋„ˆ ๋ชจ์…˜ ์ œ์–ด, ์ธก์ • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํ๋ฆ„์„ ์ •ํ™•ํžˆ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ์—๋Š” ๊ธฐ์กด C#ยทWPFยทDevExpress ๊ธฐ๋ฐ˜ ์†Œํ”„ํŠธ์›จ์–ด ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ์‚ฌ์šฉ ์ค‘์ธ ๊ณ„์ธก๊ธฐ ํ†ต์‹  ํ”„๋กœํ† ์ฝœ์„ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. .NET ๊ธฐ๋ฐ˜ ๋ฐฑ์—”๋“œ์™€ ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•ด ๋ณธ ๊ฒฝํ—˜์ด ์žˆ์–ด, ์–ธ์–ด์™€ ํ”„๋ ˆ์ž„์›Œํฌ ํ•™์Šต ์‹œ๊ฐ„๋ณด๋‹ค ๋„๋ฉ”์ธ ํ•™์Šต์— ์‹œ๊ฐ„์„ ํˆฌ์žํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. + +1๋…„ ์ฐจ์—๋Š” ๋ชจ์…˜ ์ œ์–ด ๋˜๋Š” ์•ˆํ…Œ๋‚˜ ์ธก์ • ๋ชจ๋“ˆ์—์„œ ๋‹จ์ผ ๊ธฐ๋Šฅ์„ ๋‹จ๋…์œผ๋กœ ์ฑ…์ž„์ง€๊ณ  ๊ตฌํ˜„ํ•ด, ์ธก์ • ์ž๋™ํ™” ํ๋ฆ„์˜ ํ•œ ์ถ•์„ ์•ˆ์ •์ ์œผ๋กœ ๋‹ด๋‹นํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. 3๋…„ ์ฐจ์—๋Š” ์ธก์ • ์†Œํ”„ํŠธ์›จ์–ด ๊ตฌ์กฐ ๊ฐœ์„ ์ด๋‚˜ ์‹ ๊ทœ ๊ณ„์ธก๊ธฐยทํ”„๋กœํ† ์ฝœ ํ†ตํ•ฉ ์ž‘์—…์„ ์ฃผ๋„ํ•ด, ์ƒˆ๋กœ์šด ์ธก์ • ํ™˜๊ฒฝ์„ ๋„์ž…ํ•˜๋Š” ์†๋„๋ฅผ ๋†’์ด๋Š” ๋ฐ ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +์•ˆํ…Œ๋‚˜ยทRF ๋„๋ฉ”์ธ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, ์ธก์ • ์ž๋™ํ™”์— ํ•„์š”ํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ, ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ํ๋ฆ„, ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์—ฐ๋™์€ ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์—์„œ ์ง์ ‘ ๋‹ค๋ค„์™”์Šต๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ ์ง€์‹์€ ์‚ฌ๋‚ด ํ•™์Šต๊ณผ ์‹ค๋ฌด ๊ฒฝํ—˜์„ ํ†ตํ•ด ๋น ๋ฅด๊ฒŒ ๋ณด์™„ํ•˜๋ฉด์„œ, ์ฝ”๋“œ ํ’ˆ์งˆ๊ณผ ์ธก์ • ์‹ ๋ขฐ์„ฑ์„ ํ•จ๊ป˜ ์ง€ํ‚ค๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • + +### ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ์ง€์† ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ๋กœ + +์ €๋Š” ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•ด ์™”์Šต๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ž์ฒด๊ฐ€ ๋ชฉํ‘œ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์…€ํ”„ํ˜ธ์ŠคํŒ… ํŒŒ์ผ ์„œ๋น„์Šค์ธ CloudSharp ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ, ๋‹จ์ˆœ ๊ตฌํ˜„๋งŒ์œผ๋กœ๋Š” ์šด์˜์„ ๊ฒฌ๋””๊ธฐ ์–ด๋ ต๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ง์ ‘ ๊ฒฝํ—˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ ๊ณผ์ •์—์„œ ๋™์‹œ ์š”์ฒญ์ด ๊ฐ™์€ ์ž์›์„ ๋‘๊ณ  ๊ฒฝ์Ÿํ•˜๊ฑฐ๋‚˜, ์™ธ๋ถ€ ์—…๋กœ๋“œ ์„œ๋ฒ„์˜ ์ฝœ๋ฐฑ์ด ์ค‘๋ณต ๋„๋‹ฌํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๋ฌธ์ œ๋ฅผ ํ’€๊ธฐ ์œ„ํ•ด CAS(Compare-And-Swap) ๊ธฐ๋ฐ˜ ๋™์‹œ์„ฑ ์ œ์–ด์™€ Recovery Worker ๊ตฌ์กฐ๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ SQL UPDATE ๋ฌธ์œผ๋กœ ์—…๋กœ๋“œ ์„ธ์…˜์„ ์›์ž์ ์œผ๋กœ ์ ์œ ํ•˜๊ณ , ๊ต์ฐฉ ์ƒํƒœ๋Š” ๋ณ„๋„ ์›Œ์ปค๊ฐ€ ์ž๋™์œผ๋กœ ๋ณต๊ตฌํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ์ถ”๊ฐ€ ๋ถ„์‚ฐ ๋ฝ ์ธํ”„๋ผ ์—†์ด๋„ ์ค‘๋ณต ์ฒ˜๋ฆฌ 0๊ฑด์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ๋™์‹œ ์—…๋กœ๋“œ ํ™˜๊ฒฝ์—์„œ ์ž์› ๊ฒฝ์Ÿ ๋ฌธ์ œ๋„ ํŠธ๋žœ์žญ์…˜ ๋‹จ์œ„์—์„œ ์ฐจ๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜ ์ดํ›„๋กœ๋Š” ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ๋งˆ๋‹ค โ€œ์ด ์ฝ”๋“œ๊ฐ€ ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์€ ์–ด๋””์ธ๊ฐ€โ€๋ฅผ ๋จผ์ € ๋ฌป์Šต๋‹ˆ๋‹ค. Result ํŒจํ„ด์œผ๋กœ ์‹คํŒจ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋‹ค๋ฃจ๊ณ , Clean Architecture๋กœ ๋„๋ฉ”์ธ ๊ฒฝ๊ณ„๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉฐ, ์ฃผ์š” ๊ฒฐ์ •๋งˆ๋‹ค ADR๋กœ ๊ทผ๊ฑฐ๋ฅผ ๋‚จ๊ธฐ๋Š” ๋ฐฉ์‹์ด ์ž์—ฐ์Šค๋Ÿฌ์šด ์Šต๊ด€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +์ง€๊ธˆ์˜ ์ €๋Š” ํ™”๋ คํ•œ ๊ธฐ๋Šฅ๋ณด๋‹ค ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ์™€ ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋ฅผ ์‹ ๋ขฐํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์€ ์•ˆํ…Œ๋‚˜ ์ธก์ •์ฒ˜๋Ÿผ ์ •๋ฐ€์„ฑ๊ณผ ๋ฐ˜๋ณต์„ฑ์ด ํ•ต์‹ฌ์ธ ์˜์—ญ์—์„œ ๋” ํฐ ๊ฐ€์น˜๋ฅผ ๋ฐœํœ˜ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. + +--- + +## ์ตœ์ข… ์ฒดํฌ๋ฆฌ์ŠคํŠธ + +- [x] ํšŒ์‚ฌ ์—ฐ๊ฒฐ โ€” CATR, C#/WPF, ๊ณ„์ธก๊ธฐ ์ œ์–ด ๋“ฑ ์ฑ„์šฉ๊ณต๊ณ ยทํ™ˆํŽ˜์ด์ง€ ์ •๋ณด ๋ฐ˜์˜ +- [x] ๊ฒฝํ—˜ ์ฆ๋ช… โ€” TusBlazorClient(๊ฐ•์ ), CloudSharp(์„ฑ์žฅ ๊ณผ์ •)๋กœ ์‚ฌ๋ก€ ๋ถ„๋ฆฌ +- [x] ์ˆ˜์น˜ ๊ฒฐ๊ณผ โ€” NuGet v1.0.1, 18๊ฐœ ์˜ต์…˜, 7์ข… ์ฝœ๋ฐฑ, 9๊ฐœ E2E ํ…Œ์ŠคํŠธ, ์ค‘๋ณต ์ฒ˜๋ฆฌ 0๊ฑด +- [x] ์ž…์‚ฌ ํ›„ ๊ธฐ์—ฌ โ€” ์ดˆ๊ธฐ/1๋…„/3๋…„ ๋‹จ๊ณ„๋ณ„ ๊ณ„ํš + ๋ณด์™„์  ๋ช…์‹œ +- [x] ๊ธˆ์ง€ ํ‘œํ˜„ ์ œ๊ฑฐ โ€” ์—ด์ •ยท์ตœ์„ ยท์ธ์žฌยท๋ˆ„๊ตฌ๋ณด๋‹ค ๋“ฑ ๋ฏธ์‚ฌ์šฉ +- [x] ๋‘๊ด„์‹ โ€” 4๊ฐœ ๋ฌธํ•ญ ๋ชจ๋‘ ์ฒซ ๋ฌธ์žฅ์—์„œ ๊ฒฐ๋ก  ์ œ์‹œ +- [x] ์‚ฌ๋ก€ ๋ถ„๋ฆฌ โ€” ๊ฐ™์€ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐ˜๋ณต๋˜์ง€ ์•Š์Œ \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/์•„๋ ˆ์Šค.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์•„๋ ˆ์Šค.md new file mode 100644 index 0000000..fea4c1d --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์•„๋ ˆ์Šค.md @@ -0,0 +1,63 @@ +# (์ฃผ)์•„๋ ˆ์Šค ์ž๊ธฐ์†Œ๊ฐœ์„œ + +--- + +## ์ง€์›๋™๊ธฐ + +### ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ์•ˆ์—์„œ ๋ณธ ์ต์ˆ™ํ•œ ๋ฌธ์ œ + +์ €๋Š” ๊ท€์‚ฌ์˜ VR ๋‚™ํ•˜์‚ฐ ๊ฐ•ํ•˜ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ APSยฎ๋ฅผ ์‚ดํŽด๋ณด๋ฉฐ ์ต์ˆ™ํ•œ ๋ฌธ์ œ์˜์‹์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ’ํ–ฅยทํ’์†ยท๊ธฐ์ƒ ํšจ๊ณผ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ˜์˜ํ•˜๋ฉด์„œ ๋ชจ์…˜ ์ œ์–ด์™€ 3D ๋ Œ๋”๋ง์„ ๋™๊ธฐํ™”ํ•˜๋Š” ๊ตฌ์กฐ๋Š”, ๊ฒฐ๊ตญ **๋‹ค์ค‘ ์‚ฌ๊ฑด์˜ ๋™์‹œ ์ฒ˜๋ฆฌ์™€ ์ผ๊ด€์„ฑ ๋ณด์žฅ** ๋ฌธ์ œ๋กœ ์ˆ˜๋ ดํ•ฉ๋‹ˆ๋‹ค. ์ด ์ง€์ ์ด ์ œ๊ฐ€ ๋ฐฑ์—”๋“œ์—์„œ ๋‹ค๋ค„์˜จ ์—…๋กœ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋™์‹œ์„ฑ ์ œ์–ด ๋ฌธ์ œ์™€ ๋ณธ์งˆ์ ์œผ๋กœ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +CloudSharp ํ”„๋กœ์ ํŠธ์—์„œ๋Š” tus ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜ ๋Œ€์šฉ๋Ÿ‰ ์—…๋กœ๋“œ์˜ Finalize ๋‹จ๊ณ„์—์„œ ์ค‘๋ณต ์‹คํ–‰ ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ถ„์‚ฐ ๋ฝ ์ธํ”„๋ผ ์—†์ด๋„ ๋‹จ์ผ SQL UPDATE ๊ธฐ๋ฐ˜ CAS ํŒจํ„ด์œผ๋กœ ์›์ž์  ์ ์œ ๋ฅผ ๊ตฌํ˜„ํ–ˆ๊ณ , ํŒŒ์ผ I/O์™€ DB ํŠธ๋žœ์žญ์…˜์„ ๋ถ„๋ฆฌํ•ด ์•ˆ์ „์„ฑ๊ณผ ์‘๋‹ต์„ฑ์„ ํ•จ๊ป˜ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ "์ •ํ™•์„ฑ์„ ๋ณด์žฅํ•˜๋ฉด์„œ๋„ ์‹ค์‹œ๊ฐ„์„ฑ์„ ์žƒ์ง€ ์•Š๋Š” ๊ตฌ์กฐ"๊ฐ€ ๋ฐฑ์—”๋“œ๋ฟ ์•„๋‹ˆ๋ผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜ ํ•ต์‹ฌ ๊ฐ€์น˜๋ผ๋Š” ์ ์„ ์ฒด๋“ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๊ฐ€ ๊ฐ•์กฐํ•˜๋Š” Agile & DevOps ๊ธฐ๋ฐ˜์˜ ์ง€์†์  ํ†ตํ•ฉ๊ณผ TDD ๋ฌธํ™” ๋˜ํ•œ ์ œ ๊ฐœ๋ฐœ ๋ฐฉ์‹๊ณผ ๋งž๋‹ฟ์•„ ์žˆ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ๋Š” 14๊ฐœ์˜ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜๊ณผ ADR์„ ํ•จ๊ป˜ ์šด์˜ํ•˜๊ณ  GitLab CI๋กœ ๋‹จ์œ„ยท์ธํ”„๋ผ ํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ ํ’ˆ์งˆ์„ ์ธก์ • ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฌธํ™”๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณธ ๊ฒฝํ—˜์ž…๋‹ˆ๋‹ค. + +์ด์ฒ˜๋Ÿผ ๊ท€์‚ฌ์˜ M&S ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ฐœ๋ฐœ ๋ฐฉํ–ฅ๊ณผ ์ œ๊ฐ€ ์Œ“์•„์˜จ ๋™์‹œ์„ฑยท์„ฑ๋Šฅยทํ’ˆ์งˆ ๊ด€๋ฆฌ ๊ฒฝํ—˜์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐ๋œ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ณ , ์ด๊ฒƒ์ด ์ง€์›์„ ๊ฒฐ์‹ฌํ•œ ๊ฐ€์žฅ ํฐ ์ด์œ ์ž…๋‹ˆ๋‹ค. + +--- + +## ์—…๋ฌด ์‹œ ๊ฐ•์  + +### ์›์ธ์„ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๋Š” ์‹คํ–‰๋ ฅ + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ **์‹œ์Šคํ…œ ๋ฌธ์ œ์˜ ์›์ธ์„ ๋๊นŒ์ง€ ์ถ”์ ํ•ด ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ์—ญ๋Ÿ‰**์ž…๋‹ˆ๋‹ค. + +CloudSharp ํ”„๋กœ์ ํŠธ์—์„œ tus ๊ธฐ๋ฐ˜ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ ์‹œ์Šคํ…œ์˜ Finalize ๋‹จ๊ณ„ ์ค‘๋ณต ์‹คํ–‰ ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผํ–ˆ์Šต๋‹ˆ๋‹ค. tusd hook๊ณผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ Worker ๋‘ ๊ฒฝ๋กœ์—์„œ ๊ฐ™์€ ์—…๋กœ๋“œ ์„ธ์…˜์ด ๋™์‹œ์— ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ์–ด, ํ•˜๋‚˜์˜ ํŒŒ์ผ์ด ๋‘ ๊ฐœ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋กœ ๋“ฑ๋ก๋˜๋Š” ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ํ•ด๋ฒ•์€ Redis Redlock ๊ฐ™์€ ๋ถ„์‚ฐ ๋ฝ ์ธํ”„๋ผ๋ฅผ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์ด์ง€๋งŒ, ์šด์˜ ๋ณต์žก๋„์™€ ์žฅ์•  ์ง€์ ์ด ํ•จ๊ป˜ ๋Š˜์–ด๋‚˜๋Š” ๋‹จ์ ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. + +์ €๋Š” ๋ณ„๋„ ์ธํ”„๋ผ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  **CAS(Compare-And-Swap) ํŒจํ„ด**์„ ๋‹จ์ผ SQL UPDATE๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. `UPDATE upload_session SET status='FINALIZING' WHERE status='UPLOADING'` ํ•œ ์ค„๋กœ ์›์ž์  ์ ์œ ๋ฅผ ๋ณด์žฅํ•˜๊ณ , affected_rows ๊ฐ’์œผ๋กœ ์ฒ˜๋ฆฌ ์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋А๋ฆฐ ํŒŒ์ผ I/O ๊ตฌ๊ฐ„๊ณผ ์งง์€ DB ํŠธ๋žœ์žญ์…˜ ๊ตฌ๊ฐ„์„ ๋ถ„๋ฆฌํ•ด, ๋™์‹œ์„ฑ ์ œ์–ด ์ค‘์—๋„ ๋‹ค๋ฅธ ์š”์ฒญ์ด ๋ธ”๋กœํ‚น๋˜์ง€ ์•Š๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ Recovery Worker๋ฅผ ๋‘์–ด 10๋ถ„ ์ด์ƒ FINALIZING ์ƒํƒœ์— ๋จธ๋ฌธ ์„ธ์…˜์„ ์ž๋™ ๋ณต๊ตฌํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ, ๋ณ„๋„ ๋ถ„์‚ฐ ๋ฝ ์ธํ”„๋ผ ์—†์ด **์ค‘๋ณต Finalize๋ฅผ 0๊ฑด์œผ๋กœ ์ฐจ๋‹จ**ํ–ˆ๊ณ , DB UNIQUE ์ œ์•ฝ๊ณผ ํ•จ๊ป˜ ์ด์ค‘ ์•ˆ์ „์žฅ์น˜๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. CAS ๋ฐฉ์‹์€ PostgreSQL์ด ๋ณด์žฅํ•˜๋Š” ์›์ž์„ฑ์—๋งŒ ์˜์กดํ•˜๋ฏ€๋กœ ์ถ”๊ฐ€ ์ธํ”„๋ผ ๋น„์šฉ์ด ์—†๊ณ , ์žฅ์•  ์‹œ ์ž๋™ ๋ณต๊ตฌ๊นŒ์ง€ ํฌํ•จ๋œ ๊ฒฌ๊ณ ํ•œ ๊ตฌ์กฐ์˜€์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์€ ๋‹จ์ˆœํžˆ ๋™์ž‘ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋„˜์–ด, ๋ฌธ์ œ์˜ ๋ณธ์งˆ์„ ๋ถ„์„ํ•˜๊ณ  ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๋ฉด์„œ๋„ ์•ˆ์ „ํ•œ ํ•ด๋ฒ•์„ ์„ ํƒํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฐฉ์‹์„ ์ตํžˆ๋Š” ๊ณ„๊ธฐ์˜€์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์˜ ์‹ค์‹œ๊ฐ„์„ฑ๊ณผ ์ •ํ™•์„ฑ์ด๋ผ๋Š” ๊นŒ๋‹ค๋กœ์šด ์š”๊ตฌ์‚ฌํ•ญ ์•ž์—์„œ, ์›์ธ์„ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๊ณ  ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ์•ˆ์ •์„ฑ๊ณผ ์‹ค์‹œ๊ฐ„์„ฑ์„ ํ•จ๊ป˜ + +์ž…์‚ฌ ํ›„์—๋Š” **๊ตญ๋ฐฉ M&S ๋„๋ฉ”์ธ์˜ ์•ˆ์ •์„ฑ๊ณผ ์‹ค์‹œ๊ฐ„์„ฑ์„ ํ•จ๊ป˜ ์ฑ…์ž„์ง€๋Š” SW ๊ฐœ๋ฐœ์ž**๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” 2030๋…„ M&S ๋ถ„์•ผ ์„ธ๊ณ„ ์ค‘์‹ฌ์„ ๋ชฉํ‘œ๋กœ, VRยทARยทMRยทXR ๊ธฐ์ˆ ๊ณผ ์ž์ฒด ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์—”์ง„์„ ๊ฒฐํ•ฉํ•œ ์‹ค๊ฐํ˜• ํ›ˆ๋ จ์ฒด๊ณ„๋ฅผ ํ™•์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉํ–ฅ ์•ˆ์—์„œ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์˜ ์ •ํ™•์„ฑ๊ณผ ์‹ค์‹œ๊ฐ„ ์‘๋‹ต์„ฑ์„ ๋™์‹œ์— ์ฑ…์ž„์ง€๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ์—๋Š” M&S ๋„๋ฉ”์ธ ์ง€์‹๊ณผ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. C#๊ณผ C++ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์€ ์ด๋ฏธ ๊ฐ–์ถ”๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ๊ธฐ์กด ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์—”์ง„์˜ ๊ตฌ์กฐ์™€ ํ’ํ–ฅยทํ’์†ยท๊ธฐ์ƒ ํšจ๊ณผ ๊ฐ™์€ ๋„๋ฉ”์ธ ๋ชจ๋ธ์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์šฐ์„  ๊ณผ์ œ์ž…๋‹ˆ๋‹ค. Agile ์Šคํ”„๋ฆฐํŠธ ํ๋ฆ„์— ์ ์‘ํ•˜๋ฉฐ ์ฝ”๋“œ ๋ฆฌ๋ทฐ์™€ TDD์— ์ ๊ทน์ ์œผ๋กœ ์ฐธ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +1๋…„ ์ฐจ์—๋Š” ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ชจ๋“ˆ ํ•œ ๋‹จ์œ„๋ฅผ ๋‹จ๋…์œผ๋กœ ์„ค๊ณ„ํ•˜๊ณ  ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜์ค€์— ๋„๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. CloudSharp์—์„œ ์šด์˜ํ–ˆ๋˜ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜๊ณผ ADR ์ž‘์„ฑ ๊ฒฝํ—˜์„ ์‚ด๋ ค, ํŒ€์˜ CI ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๊ฐœ์„ ์—๋„ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +3๋…„ ์ฐจ์—๋Š” ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์—”์ง„ ๊ตฌ์กฐ ๊ฐœ์„ ๊ณผ AIยทVR ๊ธฐ์ˆ  ์œตํ•ฉ ์ œ์•ˆ์— ์ฐธ์—ฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. AI SW ๊ฐœ๋ฐœ ์ง๋ฌด๊นŒ์ง€ ํ•จ๊ป˜ ๋‹ค๋ฃจ๋Š” ๊ท€์‚ฌ์˜ ํŠน์„ฑ์ƒ, ๋ฐฑ์—”๋“œ์™€ AI ์—ฐ๋™ ๊ฒฝํ—˜์„ ์‚ด๋ ค **ํ›ˆ๋ จ ๋ฐ์ดํ„ฐ ๋ถ„์„์ด๋‚˜ ํ–‰๋™ ์˜ˆ์ธก ๋ชจ๋ธ์„ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์— ์ ‘๋ชฉ**ํ•˜๋Š” ์˜์—ญ์—์„œ ๊ธฐ์—ฌํ•  ์—ฌ์ง€๊ฐ€ ํฌ๋‹ค๊ณ  ๋ด…๋‹ˆ๋‹ค. + +๊ตญ๋ฐฉ M&S ๋„๋ฉ”์ธ๊ณผ Unity 3D ์‹ค๋ฌด ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, HLA/RTI ๊ฐ™์€ M&S ํ‘œ์ค€ ์ž๋ฃŒ์™€ Unity ๊ธฐ๋ฐ˜ ๋ฌผ๋ฆฌ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ•™์Šต์„ ํ†ตํ•ด ๋ณด์™„ํ•ด ๋‚˜๊ฐ€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ๋„๋ฉ”์ธ ์ง€์‹๊ณผ ๊ธฐ์ˆ  ๊นŠ์ด๋ฅผ ๋™์‹œ์— ํ‚ค์›Œ, ๊ท€์‚ฌ์˜ "World Best" ๋น„์ „์— ๋ณดํƒฌ์ด ๋˜๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## ์„ฑ์žฅ ๊ณผ์ • + +### ๋™์ž‘์—์„œ ์ธก์ •์œผ๋กœ + +์ €์˜ ๊ฐœ๋ฐœ์ž ์ •์ฒด์„ฑ์€ "๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋Š” ๊ฒƒ"์—์„œ "๊ฒฐ๊ณผ๋ฅผ ์ธก์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ"์œผ๋กœ ๊ด€์ ์ด ๋ฐ”๋€ ํ•œ ์‹œ์ ์—์„œ ํ˜•์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ์š”๊ตฌ์‚ฌํ•ญ๋Œ€๋กœ ํ™”๋ฉด์ด ๊ทธ๋ ค์ง€๊ณ  API๊ฐ€ ์‘๋‹ตํ•˜๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ด€์ ์ด ๋ฐ”๋€ ๊ณ„๊ธฐ๋Š” .NET Blazor ํ™˜๊ฒฝ์—์„œ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ **TusBlazorClient ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ**๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด NuGet์— ๋ฐฐํฌํ•˜๋ฉด์„œ์˜€์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ œ๊ฐ€ ์“ฐ๋Š” ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๊ฐ€ ์“ฐ๋Š” ์ฝ”๋“œ์ด๋ฏ€๋กœ, ๋™์ž‘ ์—ฌ๋ถ€๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ–ˆ์Šต๋‹ˆ๋‹ค. C# ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์ง๋ ฌํ™” ๋ถˆ๊ฐ€ ๋ฌธ์ œ, JS ๋ชจ๋“ˆ ์ƒ๋ช…์ฃผ๊ธฐ, ์˜ต์…˜ ๋™์  ๋ณ€๊ฒฝ ๋“ฑ ์‚ฌ์šฉ์ž๊ฐ€ ๋งˆ์ฃผ์น  ์ˆ˜ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์˜ˆ์ธกํ•˜๊ณ  Selenium E2E ํ…Œ์ŠคํŠธ๋กœ ๊ฒ€์ฆํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์€ CloudSharp ํ”„๋กœ์ ํŠธ์—์„œ ๋” ๊นŠ์–ด์กŒ์Šต๋‹ˆ๋‹ค. tus ๊ธฐ๋ฐ˜ ์—…๋กœ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์˜ Finalize ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผํ–ˆ์„ ๋•Œ, "์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๊ฒŒ ํ• ๊นŒ"๊ฐ€ ์•„๋‹ˆ๋ผ "์–ด๋–ค ์กฐ๊ฑด์—์„œ ๊นจ์งˆ ์ˆ˜ ์žˆ๋Š”๊ฐ€"๋ถ€ํ„ฐ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค. CAS ํŒจํ„ด์œผ๋กœ ๋ถ„์‚ฐ ๋ฝ ์—†์ด ์›์ž์  ์ ์œ ๋ฅผ ๋ณด์žฅํ•˜๊ณ , Recovery Worker๋กœ ๊ต์ฐฉ ์ƒํƒœ๋ฅผ ์ž๋™ ๋ณต๊ตฌํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— 14๊ฐœ์˜ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜ ๋ฌธ์„œ์™€ ADR์„ ์ •๋ฆฌํ•˜๋ฉฐ **๊ฒฐ์ •์˜ ์ด์œ ๊นŒ์ง€ ์ธก์ • ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋‚จ๊ธฐ๋Š” ์Šต๊ด€**์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +์ง€๊ธˆ์˜ ์ €๋Š” ์ƒˆ๋กœ์šด ๋ฌธ์ œ๋ฅผ ๋งŒ๋‚˜๋ฉด ๊ฐ€์žฅ ๋จผ์ € ์›์ธ ๋ถ„์„๊ณผ ์ธก์ • ์ง€ํ‘œ๋ถ€ํ„ฐ ์ •์˜ํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„๋ณด๋‹ค ์‹œ์Šคํ…œ์ด ๊นจ์งˆ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ๊ณ„ ์กฐ๊ฑด์— ๊ด€์‹ฌ์„ ๋‘๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์น˜์™€ ๋ฌธ์„œ๋กœ ๋‚จ๊ธฐ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ท€์‚ฌ์˜ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ๊ฐœ๋ฐœ์—์„œ๋„ ์ •ํ™•์„ฑยท์‹ค์‹œ๊ฐ„์„ฑยท์•ˆ์ •์„ฑ์ด๋ผ๋Š” ๊นŒ๋‹ค๋กœ์šด ์š”๊ตฌ์‚ฌํ•ญ์„ ์ธก์ • ๊ฐ€๋Šฅํ•œ ํ˜•ํƒœ๋กœ ๋‹ค๋ฃจ๋Š” ๋ฐ ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/์—์ดํˆฌํ….md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์—์ดํˆฌํ….md new file mode 100644 index 0000000..ee1c747 --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์—์ดํˆฌํ….md @@ -0,0 +1,78 @@ +# ์—์ดํˆฌํ… ์ž๊ธฐ์†Œ๊ฐœ์„œ +## 1. ์ง€์›๋™๊ธฐ + +### ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ์—์„œ ์ถœ๋ฐœํ•˜๋Š” AI ์†”๋ฃจ์…˜ + +์—์ดํˆฌํ…์ด ๊ฐ•์กฐํ•˜๋Š” "ํ˜„์—… ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” AI ๋ชจ๋ธ" ๊ฐœ๋ฐœ ๋ฐฉํ–ฅ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +AX ์„œ๋น„์Šค ํŽ˜์ด์ง€์—์„œ SOTA ๋ชจ๋ธ ์กฐ์‚ฌ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต, ํ•™์Šต, ๊ฒ€์ฆ, REST API ๊ตฌ์ถ•๊นŒ์ง€ ์ „ ๊ณผ์ •์„ ์ง์ ‘ ์ˆ˜ํ–‰ํ•œ๋‹ค๋Š” ์ ์ด ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ Problem ํŽ˜์ด์ง€์—์„œ ์ข…์ด ๊ธฐ๋ฐ˜ ์„ค๋น„ ์œ ์ง€๋ณด์ˆ˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์ค€ํ™”ํ•˜๊ณ  ํ†ตํ•ฉ DB๋กœ ์—ฐ๊ฒฐํ•œ ๋’ค ์˜ˆ์ง€์ •๋น„ ๋ชจ๋ธ์„ ์ ์šฉํ•œ ์‚ฌ๋ก€๋Š”, ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ์ด ๊ณง AI ๊ฒฐ๊ณผ์˜ ํ’ˆ์งˆ์ด๋ผ๋Š” ๋ฉ”์‹œ์ง€๋กœ ๋‹ค๊ฐ€์™”์Šต๋‹ˆ๋‹ค. + +์ด ๋ฐฉํ–ฅ์€ ์ œ๊ฐ€ ์ˆ ํ†ต์—ฌ์ง€๋„ ํ”„๋กœ์ ํŠธ์—์„œ ๋ถ€๋”ชํžŒ ๋ฌธ์ œ์˜์‹๊ณผ ๋งž๋‹ฟ์•„ ์žˆ์Šต๋‹ˆ๋‹ค. GPT-5.2 ๊ธฐ๋ฐ˜ ์ˆ ์ง‘ ์ถ”์ฒœ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค๋ฉด์„œ, ๋‹จ์ˆœํžˆ ๋ชจ๋ธ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋˜์ง€๋Š” ๋ฐฉ์‹์œผ๋กœ๋Š” hallucination๊ณผ ํ† ํฐ ๋น„์šฉ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ๋ž˜์„œ ๋„๋ฉ”์ธ ํŠนํ™” Pipe-delimited Format์„ ์„ค๊ณ„ํ•ด JSON ๋Œ€๋น„ ํ† ํฐ์„ ์•ฝ 40% ์ ˆ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ •๊ทœ์‹ ๊ธฐ๋ฐ˜ ํ—ˆ์šฉ ID Set ๊ฒ€์ฆ์œผ๋กœ GPT๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ˆ ์ง‘ ์ถ”์ฒœ์„ 0๊ฑด์œผ๋กœ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ API ์žฅ์• ์— ๋Œ€๋น„ํ•œ ๊ฑฐ๋ฆฌ์ˆœ ํด๋ฐฑ์„ ๋‘์–ด, AI๊ฐ€ ์‹คํŒจํ•ด๋„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ธฐ๋ณธ ๊ฒฐ๊ณผ๋Š” ๋ฐ˜ํ™˜๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์€ AI๋ฅผ ๋‹จ์ˆœํžˆ ํ˜ธ์ถœํ•˜๋Š” ๊ฐœ๋ฐœ์ด ์•„๋‹ˆ๋ผ, ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ๊ณผ ์šด์˜ ์•ˆ์ •์„ฑ๊นŒ์ง€ ์ฑ…์ž„์ง€๋Š” ๊ฐœ๋ฐœ์ด ๋ฌด์—‡์ธ์ง€ ๊ณ ๋ฏผํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +์—์ดํˆฌํ…์˜ ํ์‡„ํ˜• AI ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ๋””์ง€ํ„ธ ์ „ํ™˜ ์†”๋ฃจ์…˜์€, ๋ฐ์ดํ„ฐ ์ •์ œ๋ถ€ํ„ฐ ๋ชจ๋ธ ์šด์˜๊นŒ์ง€ ํ†ตํ•ฉ์ ์œผ๋กœ ๋‹ค๋ฃจ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ€์žฅ ํ•„์š”ํ•œ ์˜์—ญ์ด๋ผ ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋™์•ˆ ์Œ“์•„์˜จ ๋ฐ์ดํ„ฐ ์ง๋ ฌํ™” ์ตœ์ ํ™”์™€ AI ์‘๋‹ต ๊ฒ€์ฆ ๊ฒฝํ—˜์ด ํšŒ์‚ฌ์˜ ๋ฐฉํ–ฅ๊ณผ ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ณ , ์ด ์ ์ด ์ง€์›์„ ๊ฒฐ์‹ฌํ•œ ๊ฐ€์žฅ ํฐ ์ด์œ ์ž…๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### AI์˜ ํ•œ๊ณ„๋ฅผ ์ฝ”๋“œ๋กœ ๋ณด์™„ํ•˜๋‹ค + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ์™ธ๋ถ€ AI API์˜ ํ•œ๊ณ„๋ฅผ ์‹œ์Šคํ…œ ์„ค๊ณ„๋กœ ๋ณด์™„ํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +์ˆ ํ†ต์—ฌ์ง€๋„ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ์ž์˜ ์œ„์น˜, ๋‚ ์”จ, ์‹œ๊ฐ„๋Œ€, ์š”์ฒญ์‚ฌํ•ญ์„ GPT-5.2์— ์ „๋‹ฌํ•ด ๋ฐ˜๊ฒฝ ๋‚ด ์ˆ ์ง‘์„ ์ถ”์ฒœํ•˜๋Š” ๋ฐฑ์—”๋“œ ์ถ”์ฒœ ์—”์ง„์„ ๋‹จ๋…์œผ๋กœ ๋‹ด๋‹นํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” ์„ธ ๊ฐ€์ง€์˜€์Šต๋‹ˆ๋‹ค. ํ›„๋ณด ์ˆ ์ง‘์ด ์ตœ๋Œ€ 200๊ฐœ์— ๋‹ฌํ•ด ํ•œ ๋ฒˆ์— GPT์— ์ „๋‹ฌํ•˜๋ฉด 16,000ํ† ํฐ์„ ์ดˆ๊ณผํ•˜์—ฌ ์‘๋‹ต ํ’ˆ์งˆ์ด ์ €ํ•˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ GPT๊ฐ€ ์ž…๋ ฅ ํ›„๋ณด์— ์—†๋Š” ์ˆ ์ง‘์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” hallucination์ด ๋ฐœ์ƒํ–ˆ๊ณ , JSON ์ง๋ ฌํ™”์˜ ๊ตฌ์กฐ ๋ฌธ์ž๊ฐ€ ๋ถˆํ•„์š”ํ•œ ํ† ํฐ์„ ์†Œ๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์„ธ ๊ฐ€์ง€ ์„ค๊ณ„๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ฒซ์งธ, 2๋‹จ๊ณ„ Cascade Ranking ๊ตฌ์กฐ๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. 1๋‹จ๊ณ„์—์„œ 200๊ฐœ๋ฅผ 100๊ฐœ์”ฉ ๋ฐฐ์น˜๋กœ ๋‚˜๋ˆ  top5๋ฅผ ์„ ๋ณ„ํ•˜๊ณ , 2๋‹จ๊ณ„์—์„œ ์ตœ๋Œ€ 40๊ฐœ๋ฅผ ์ •๋ฐ€ ๋žญํ‚นํ•ด Top 10๊ณผ ์ถ”์ฒœ ์ด์œ ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ํ˜ธ์ถœ๋‹น ํ† ํฐ์„ ์•ฝ 70% ์ ˆ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +๋‘˜์งธ, Defensive Normalization์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •๊ทœ์‹์œผ๋กœ ์ž…๋ ฅ์—์„œ ํ—ˆ์šฉ ID Set์„ ์ถ”์ถœํ•œ ๋’ค, GPT ์‘๋‹ต์˜ barId๋ฅผ ๋Œ€์กฐํ•ด ๋ฏธํ—ˆ์šฉ ID๋ฅผ ์ฆ‰์‹œ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ตฌ์กฐ๋กœ hallucination์— ์˜ํ•œ ์ž˜๋ชป๋œ ์ถ”์ฒœ์„ 0๊ฑด์œผ๋กœ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์…‹์งธ, ๋„๋ฉ”์ธ ํŠนํ™” Pipe-delimited Format์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. `B|id=123|n=ํฌ์ฐจ` ํ˜•์‹์œผ๋กœ ์ง๋ ฌํ™”ํ•˜๊ณ  ๋ชจ๋“  ํ•„๋“œ์—์„œ ํŒŒ์ดํ”„์™€ ๊ฐœํ–‰์„ sanitizeํ–ˆ์Šต๋‹ˆ๋‹ค. JSON ๋Œ€๋น„ ํ† ํฐ์„ ์•ฝ 40% ์ ˆ๊ฐํ–ˆ๊ณ  ํŒŒ์‹ฑ ์˜ค๋ฅ˜๋„ ํ•จ๊ป˜ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์„ ํ†ตํ•ด ์™ธ๋ถ€ ์˜์กด์„ฑ์˜ ์‹คํŒจ ๊ฐ€๋Šฅ์„ฑ๊ณผ ๋น„์šฉ๊นŒ์ง€ ์‹œ์Šคํ…œ์— ๋ฐ˜์˜ํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฐฉ์‹์„ ์ตํ˜”์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํšŒ์‚ฌ AI ์†”๋ฃจ์…˜์˜ ์•ˆ์ •์„ฑ๊ณผ ๋น„์šฉ ํšจ์œจ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ๋ฐ์ดํ„ฐ๋ถ€ํ„ฐ ๋ชจ๋ธ๊นŒ์ง€ ์ฑ…์ž„์ง€๋Š” ๊ฐœ๋ฐœ์ž + +์ž…์‚ฌ ํ›„์—๋Š” ์—์ดํˆฌํ…์˜ ๋””์ง€ํ„ธ ์ „ํ™˜ ์†”๋ฃจ์…˜ ์•ˆ์—์„œ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ๊ณผ AI ์„œ๋น™์„ ํ•จ๊ป˜ ๋‹ค๋ฃจ๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +ํšŒ์‚ฌ๊ฐ€ ๊ฐ•์กฐํ•˜๋Š” "๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์˜์‚ฌ๊ฒฐ์ • ์ž๋™ํ™”"๋Š” ์ •์ œ๋œ ๋ฐ์ดํ„ฐ, ์•ˆ์ •์ ์ธ ๋ชจ๋ธ ํ•™์Šต, ์‹คํŒจ์— ๊ฐ•ํ•œ ์„œ๋น™ ๊ตฌ์กฐ ์„ธ ์ถ•์ด ๋งž๋ฌผ๋ ค์•ผ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ์—๋Š” ๊ธฐ์กด AX ์†”๋ฃจ์…˜๊ณผ ๋น…๋ฐ์ดํ„ฐ ํ”Œ๋žซํผ์˜ ์ฝ”๋“œ์™€ ๋ฌธ์„œ๋ฅผ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ํ์‡„ํ˜• AI ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ๋ฅด๋Š” ๊ฒฝ๋กœ์™€ REST API ์„œ๋น™ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +1๋…„ ์ฐจ์—๋Š” AI ๋ชจ๋ธ๊ณผ ๋ฐฑ์—”๋“œ API๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์˜์—ญ์—์„œ ๋‹จ๋…์œผ๋กœ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ˆ ํ†ต์—ฌ์ง€๋„์—์„œ ์ ์šฉํ•œ ์ž…๋ ฅ ๊ฒ€์ฆ, ์‘๋‹ต ์ •๊ทœํ™”, Graceful Degradation ํŒจํ„ด์„ ์‚ฌ๋‚ด ์†”๋ฃจ์…˜์— ๋งž๊ฒŒ ๋ฐœ์ „์‹œ์ผœ ์šด์˜ ์•ˆ์ •์„ฑ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +3๋…„ ์ฐจ์—๋Š” ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ ๊ฒ€์ฆ๊ณผ AI ์‘๋‹ต ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์ž๋™ํ™”ํ•˜๋Š” ๊ตฌ์กฐ ๊ฐœ์„ ์„ ์ œ์•ˆํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‹จ๋ฐœ์„ฑ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์— ๋จธ๋ฌด๋ฅด์ง€ ์•Š๊ณ , ํŒ€ ์ „์ฒด๊ฐ€ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ์„ ๋” ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“œ๋Š” ๋ฐ ๋ณดํƒฌ์ด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +๋Œ€๊ทœ๋ชจ ๋ถ„์‚ฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, ํ˜„์žฌ Hadoop๊ณผ Spark ํ•™์Šต์„ ์ง„ํ–‰ํ•˜๋ฉฐ ๋น…๋ฐ์ดํ„ฐ ํ”Œ๋žซํผ์˜ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ตํžˆ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ Didit ํ˜‘์—… ํ”Œ๋žซํผ์—์„œ Redis ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์ž‘์—… ํ์™€ Pub/Sub ๋ฉ”์‹œ์ง•์„ ์ง์ ‘ ์„ค๊ณ„ํ•˜๋ฉฐ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌ ๊ฐ๊ฐ์„ ๋ณด์™„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +์žฅ๊ธฐ์ ์œผ๋กœ๋Š” ํšŒ์‚ฌ๊ฐ€ ์ง„ํ–‰ํ•˜๋Š” ์Šค๋งˆํŠธํŒฉํ† ๋ฆฌ์™€ ์Šค๋งˆํŠธ์‹œํ‹ฐ ์†”๋ฃจ์…˜์—์„œ, ํ˜„์žฅ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ •์ ์œผ๋กœ ๋ฐ›์•„๋“ค์ด๊ณ  AI ๋ชจ๋ธ๋กœ ๊ฐ€์น˜๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์„ ํ•จ๊ป˜ ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • + +### ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž + +์ €๋Š” ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ณด๋‹ค ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋” ์ค‘์š”ํ•˜๊ฒŒ ์—ฌ๊ธฐ๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. + +๋ฐฑ์—”๋“œ๋ฅผ ์ฒ˜์Œ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋ฉด ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Didit ํ˜‘์—… ํ”Œ๋žซํผ์—์„œ GitHub ์ด์Šˆ๋ฅผ AI๋กœ ๋ถ„์„ํ•ด ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋งค๊ธฐ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ด ์ƒ๊ฐ์ด ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค. + +ML ์ถ”๋ก ์€ ์ˆ˜ ์ดˆ์—์„œ ์ˆ˜์‹ญ ์ดˆ๊ฐ€ ๊ฑธ๋ ค ์ผ๋ฐ˜ HTTP ์š”์ฒญ-์‘๋‹ต ์‚ฌ์ดํด๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ฒ˜์Œ์—๋Š” ๋‹จ์ˆœํ•œ ๋™๊ธฐ ํ˜ธ์ถœ๋กœ ๊ตฌํ˜„ํ–ˆ์ง€๋งŒ ์‘๋‹ต ์ง€์—ฐ์œผ๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋ฌด๋„ˆ์กŒ์Šต๋‹ˆ๋‹ค. + +์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Redis List๋ฅผ ์ž‘์—… ํ๋กœ, Redis Pub/Sub์„ ๊ฒฐ๊ณผ ์•Œ๋ฆผ์œผ๋กœ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” SSE๋กœ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•˜์—ฌ ML ์ถ”๋ก  ์‹œ๊ฐ„๊ณผ API ์‘๋‹ต์„ฑ์„ ์™„์ „ํžˆ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์ž‘์ด ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž๊ฐ€ ์ฒด๊ฐํ•˜๋Š” ์‹œ๊ฐ„์ด ์ง„์งœ ๊ฒฐ๊ณผ๋ผ๋Š” ์ ์„ ๊ทธ๋•Œ ์ฒ˜์Œ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜ ์ดํ›„ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ๋•Œ ํ•ญ์ƒ ์™ธ๋ถ€ ์˜์กด์„ฑ์˜ ์‹คํŒจ์™€ ๋น„์šฉ์„ ํ•จ๊ป˜ ๊ณ ๋ฏผํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. CloudSharp ํŒŒ์ผ ์„œ๋น„์Šค์—์„œ๋Š” ์—…๋กœ๋“œ Finalize ์ค‘๋ณต ์‹คํ–‰ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CAS ๊ธฐ๋ฐ˜ ์›์ž์  ์ ์œ ์™€ Recovery Worker๋ฅผ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ˆ ํ†ต์—ฌ์ง€๋„์—์„œ๋Š” GPT ์‘๋‹ต์˜ hallucination๊ณผ ํ† ํฐ ๋น„์šฉ์„ ์ฝ”๋“œ ์ˆ˜์ค€์—์„œ ๋ฐฉ์–ดํ–ˆ์Šต๋‹ˆ๋‹ค. + +๋˜ํ•œ ๋„๋ฉ”์ธ ๋ชจ๋ธ๋ง๊ณผ ํด๋ฆฐ ์•„ํ‚คํ…์ฒ˜์—๋„ ๊ด€์‹ฌ์„ ๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ๋Š” Api/Core/Infrastructure ์˜์กด์„ฑ ๋ฐฉํ–ฅ์„ ์—„๊ฒฉํžˆ ๋ถ„๋ฆฌํ•˜๊ณ , Result ํŒจํ„ด์œผ๋กœ ์„ฑ๊ณต๊ณผ ์‹คํŒจ๋ฅผ ๊ฐ’์œผ๋กœ ํ‘œํ˜„ํ•ด ํ˜ธ์ถœ๋ถ€์˜ ์˜ˆ์™ธ ๋ˆ„๋ฝ์„ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ์ฝ”๋“œ๋ผ๋„ ๊ตฌ์กฐ์— ๋”ฐ๋ผ ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ์ด ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง„๋‹ค๋Š” ์ ์„ ์ง์ ‘ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +ํ˜„์žฌ์˜ ์ €๋Š” ์™ธ๋ถ€ ์‹œ์Šคํ…œ์˜ ํ•œ๊ณ„๋ฅผ ์ธ์ •ํ•˜๊ณ  ์ฝ”๋“œ์™€ ๊ตฌ์กฐ๋กœ ๋ณด์™„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž, ๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์น˜๋กœ ์ธก์ •ํ•ด ๋‹ค์Œ ๊ฐœ์„ ์˜ ๊ทผ๊ฑฐ๋กœ ์‚ผ๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/์œ„์กด.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์œ„์กด.md new file mode 100644 index 0000000..8dc6eb5 --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์œ„์กด.md @@ -0,0 +1,73 @@ +# ใˆœ์œ„์กด ์†”๋ฃจ์…˜ ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ + +--- + +## 1. ์ง€์›๋™๊ธฐ + +### ๋‘ ์Šคํƒ๊ณผ AI ๊ฒฝํ—˜์ด ํ•œ ๊ณณ์—์„œ ๋งŒ๋‚˜๋Š” ์ž๋ฆฌ + +์œ„์กด์˜ ์†”๋ฃจ์…˜ ๊ฐœ๋ฐœ์ž ์ฑ„์šฉ ๊ณต๊ณ ๋ฅผ ๋ณธ ์ˆœ๊ฐ„, ์ง€๋‚œ 2๋…„๊ฐ„ ๋”ฐ๋กœ ์Œ“์•„์˜จ ๊ฒฝํ—˜์ด ํ•œ ์ž๋ฆฌ์—์„œ ์—ฐ๊ฒฐ๋  ์ˆ˜ ์žˆ๊ฒ ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” EHSยทLIMSยท์ถœ์ž…๊ด€๋ฆฌ ๊ฐ™์€ ์‚ฐ์—…์šฉ ์†”๋ฃจ์…˜์„ Java/Spring๊ณผ C#/ASP.NET MVC ์–‘์ชฝ ์Šคํƒ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ๋Š” Vue.js๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, AI/LLM ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์„ ์šฐ๋Œ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” Spring Boot ๊ธฐ๋ฐ˜ ํ˜‘์—… ํ”Œ๋žซํผ(Didit)๊ณผ GPT-5.2 ๊ธฐ๋ฐ˜ ์ถ”์ฒœ ์„œ๋น„์Šค(์ˆ ํ†ต์—ฌ์ง€๋„)๋ฅผ Java๋กœ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— ASP.NET Core ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ €์žฅ ์„œ๋น„์Šค(CloudSharp)์™€ Blazor์šฉ ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(TusBlazorClient, NuGet v1.0.1 ๋ฐฐํฌ)๋ฅผ C#์œผ๋กœ ๊ฐœ๋ฐœํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ๋Š” Vue 3 + TypeScript + Pinia๋กœ ์ง€๋„ UI์™€ AI ์ถ”์ฒœ ๊ฒฐ๊ณผ๋ฅผ ๋™๊ธฐํ™”ํ•˜๋Š” ํ™”๋ฉด์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. + +ํŠนํžˆ SHE ์‹œ์Šคํ…œ ํŽ˜์ด์ง€์˜ "Web ๊ธฐ๋ฐ˜ ๋ชจ๋“ˆ์‹ ๊ตฌ์กฐ๋กœ TMSยทLIMSยทRTDB ๋“ฑ ๋‹ค์–‘ํ•œ ์‹œ์Šคํ…œ๊ณผ ์ธํ„ฐํŽ˜์ด์Šค๋˜๋ฉฐ, ํ†ตํ•ฉ ๋˜๋Š” ๊ฐœ๋ณ„ ๋ชจ๋“ˆ ๊ตฌ์ถ•์ด ๊ฐ€๋Šฅํ•˜๋‹ค"๋Š” ์„ค๋ช…์— ์ฃผ๋ชฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ œ๊ฐ€ CloudSharp์—์„œ Clean Architecture๋กœ 12๊ฐœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ชจ๋“ˆ๋Ÿฌ ๋ชจ๋†€๋ฆฌ์Šค๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , Storage Provider๋ฅผ Local FSยทMinIOยทS3๋กœ ์ถ”์ƒํ™”ํ•œ ๊ฒฝํ—˜๊ณผ ์ง์ ‘ ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ๋А๊ผˆ์Šต๋‹ˆ๋‹ค. + +๋˜ํ•œ ๊ท€์‚ฌ๊ฐ€ Green IT & ESG์™€ AI ๊ธฐ๋ฐ˜ DX/AX๋กœ ์‚ฌ์—… ์˜์—ญ์„ ํ™•์žฅํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ ๋„ ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. ์ˆ ํ†ต์—ฌ์ง€๋„์—์„œ GPT ํ˜ธ์ถœ ํ† ํฐ์„ 70% ์ ˆ๊ฐํ•œ 2๋‹จ๊ณ„ Cascade Ranking ์„ค๊ณ„ ๊ฒฝํ—˜์ด, LIMS์˜ ํ’ˆ์งˆ ๋ฐ์ดํ„ฐ ๋ถ„์„์ด๋‚˜ SHE์˜ ์‚ฌ๊ณ  ์˜ˆ์ธก ๊ฐ™์€ ์‚ฐ์—… ์†”๋ฃจ์…˜์˜ AI ๋„์ž… ๋‹จ๊ณ„์—์„œ ์˜๋ฏธ ์žˆ๊ฒŒ ํ™œ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด์ฒ˜๋Ÿผ ๋‘ ์Šคํƒ์„ ๋ชจ๋‘ ๋‹ค๋ค„๋ณธ ๊ฒฝํ—˜๊ณผ AI ์—”์ง€๋‹ˆ์–ด๋ง ๊ฒฝํ—˜์ด ๊ท€์‚ฌ์˜ ์‚ฐ์—…์šฉ ์†”๋ฃจ์…˜ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ์ง์ ‘ ์—ฐ๊ฒฐ๋œ๋‹ค๊ณ  ๋ณด๊ณ  ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### 200๊ฐœ ํ›„๋ณด๋ฅผ GPT์— ํ•œ ๋ฒˆ์— ๋ณด๋‚ผ ์ˆ˜ ์—†์„ ๋•Œ + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ์˜ ํ•œ๊ณ„๋ฅผ ์ธก์ • ๊ฐ€๋Šฅํ•œ ์ˆ˜์น˜๋กœ ํ’€์–ด๋‚ด๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +์ˆ ํ†ต์—ฌ์ง€๋„ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ์ž์˜ ์œ„์น˜ยท๋‚ ์”จยท์‹œ๊ฐ„๋Œ€๋ฅผ ๋ฐ˜์˜ํ•ด ๋ฐ˜๊ฒฝ ๋‚ด ์ตœ๋Œ€ 200๊ฐœ ์ˆ ์ง‘ ์ค‘ Top 10์„ ์ถ”์ฒœํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ธฐ ๊ตฌํ˜„์—์„œ๋Š” 200๊ฐœ๋ฅผ ํ•œ ๋ฒˆ์— GPT์— ์ „๋‹ฌํ–ˆ๋Š”๋ฐ, ์ž…๋ ฅ ํ† ํฐ์ด 16,000๊ฐœ๋ฅผ ๋„˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ๊ฐ€ ๊ธธ์–ด์งˆ์ˆ˜๋ก GPT์˜ attention ํ’ˆ์งˆ์ด ์ €ํ•˜๋˜์–ด ์ถ”์ฒœ์ด ๊ฑฐ๋ฆฌ์ˆœ๊ณผ ํฐ ์ฐจ์ด๊ฐ€ ์—†๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. + +์›์ธ์€ ๋‘ ๊ฐ€์ง€์˜€์Šต๋‹ˆ๋‹ค. ์ฒซ์งธ, JSON์˜ ํ‚ค ์ด๋ฆ„๊ณผ ๋”ฐ์˜ดํ‘œ ๊ฐ™์€ ๊ตฌ์กฐ ๋ฌธ์ž๊ฐ€ ํ† ํฐ์„ ๋‚ญ๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‘˜์งธ, ํ›„๋ณด๊ฐ€ 200๊ฐœ์— ๋‹ฌํ•˜๋ฉด GPT๊ฐ€ ํ•ต์‹ฌ ํ›„๋ณด์— ์ง‘์ค‘ํ•˜๊ธฐ ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค. + +์ €๋Š” ๋‘ ๋‹จ๊ณ„๋กœ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ๋„๋ฉ”์ธ ํŠนํ™” Pipe-delimited ํฌ๋งท(`B|id=123|c=์ฃผ์ |n=ํฌ์ฐจ`)์„ ์„ค๊ณ„ํ•ด JSON ๋Œ€๋น„ ์•ฝ 40% ํ† ํฐ์„ ์ ˆ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์œผ๋กœ 2๋‹จ๊ณ„ Cascade Ranking์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. 1๋‹จ๊ณ„๋Š” 100๊ฐœ์”ฉ ๋ฐฐ์น˜๋กœ ๋‚˜๋ˆ  ID๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ๋Ÿ‰ ํ˜ธ์ถœ๋กœ ํ›„๋ณด๋ฅผ 40๊ฐœ๋กœ ์••์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. 2๋‹จ๊ณ„์—์„œ ์ •๋ฐ€ ๋žญํ‚น๊ณผ ์ž์—ฐ์–ด ์ถ”์ฒœ ์ด์œ ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ถ”๊ฐ€๋กœ GPT๊ฐ€ ์ž…๋ ฅ์— ์—†๋Š” ๊ฐ€๊ฒŒ ID๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” hallucination์„ ๋ง‰์•„์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ •๊ทœ์‹ `(?m)^B\|id=(\d+)\b`๋กœ ์ž…๋ ฅ B ๋ผ์ธ์—์„œ ํ—ˆ์šฉ ID Set์„ ์ถ”์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต์˜ barId๋ฅผ ๋Œ€์กฐํ•ด ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ID๋ฅผ ์ฆ‰์‹œ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. AI ํ˜ธ์ถœ ์‹คํŒจ ์‹œ์—๋Š” ๊ฑฐ๋ฆฌ์ˆœ ํด๋ฐฑ์œผ๋กœ ์„œ๋น„์Šค ์ค‘๋‹จ์„ ๋ง‰์•˜์Šต๋‹ˆ๋‹ค. + +๊ฒฐ๊ณผ์ ์œผ๋กœ API ํ˜ธ์ถœ๋‹น ํ† ํฐ์„ ์•ฝ 70% ์ ˆ๊ฐํ•˜๊ณ , ์ž˜๋ชป๋œ ๊ฐ€๊ฒŒ๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ์‚ฌ๊ณ ๋ฅผ 0๊ฑด์œผ๋กœ ์œ ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค. AI ์žฅ์•  ์‹œ์—๋„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋นˆ ํ™”๋ฉด์ด ์•„๋‹Œ ๊ธฐ๋ณธ ์ถ”์ฒœ์ด ์ œ๊ณต๋˜๋„๋ก ๊ฐ€์šฉ์„ฑ์„ ํ™•๋ณดํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์€ ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ๋‹ค๋ฃฐ ๋•Œ "์ธก์ • ๊ฐ€๋Šฅํ•œ ์ง€ํ‘œ โ†’ ๋‹จ๊ณ„๋ณ„ ๋ถ„๋ฆฌ โ†’ ๋ฐฉ์–ด ์ฝ”๋“œ โ†’ ํด๋ฐฑ"์ด๋ผ๋Š” ์ ˆ์ฐจ๋ฅผ ์ตํžˆ๊ฒŒ ํ•ด์คฌ์Šต๋‹ˆ๋‹ค. ๊ท€์‚ฌ์˜ LIMSยทSHE ์†”๋ฃจ์…˜๋„ TMSยทRTDBยทHMI ๋“ฑ ๋‹ค์–‘ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๋งŒํผ, ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์—ฐ๋™์„ ์•ˆ์ •์ ์œผ๋กœ ๋‹ค๋ฃจ๋Š” ์—ญ๋Ÿ‰์œผ๋กœ ์ž…์‚ฌ ์งํ›„๋ถ€ํ„ฐ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ๋„๋ฉ”์ธ์„ ์ดํ•ดํ•˜๋Š” ์†”๋ฃจ์…˜ ๊ฐœ๋ฐœ์ž๋กœ + +์ž…์‚ฌ ํ›„์—๋Š” ์ œ์กฐยทํ™”ํ•™ ์‚ฐ์—…์˜ ๋„๋ฉ”์ธ ์ง€์‹์„ ๊ฐ–์ถ˜ ์†”๋ฃจ์…˜ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” EHSยทLIMSยท์ถœ์ž…๊ด€๋ฆฌ ์†”๋ฃจ์…˜์„ ํ†ตํ•ฉ ๋˜๋Š” ๋ชจ๋“ˆ ๋‹จ์œ„๋กœ ์ œ๊ณตํ•˜๋ฉฐ, ์ตœ๊ทผ Green IT & ESG์™€ AI ๊ธฐ๋ฐ˜ DX/AX๋กœ ์˜์—ญ์„ ํ™•์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์ด ๋ฐฉํ–ฅ ์•ˆ์—์„œ "๊ธฐ์ˆ ์„ ์‚ฐ์—… ๋ฌธ์ œ์— ์ •ํ™•ํžˆ ์ ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž"๊ฐ€ ๋˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ฒซ 6๊ฐœ์›”์€ ๋„๋ฉ”์ธ ์ดํ•ด์— ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. EHS์™€ LIMS์˜ ํ•ต์‹ฌ ์—…๋ฌด ํ๋ฆ„, ์‚ฐ์—…์•ˆ์ „๋ณด๊ฑด๋ฒ•๊ณผ ํ™”ํ•™๋ฌผ์งˆ๊ด€๋ฆฌ๋ฒ• ๋“ฑ ๊ด€๋ จ ๊ทœ์ œ, ๊ธฐ์กด ์†”๋ฃจ์…˜์˜ ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ๋ชจ๋“ˆ ๊ตฌ์กฐ๋ฅผ ํ•™์Šตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— Spring๊ณผ ASP.NET ์–‘์ชฝ ํ™˜๊ฒฝ์—์„œ ์ž‘์€ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ๋‹จ๋…์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ํŒ€์˜ ์ฝ”๋”ฉ ์ปจ๋ฒค์…˜์„ ์ตํžˆ๊ฒ ์Šต๋‹ˆ๋‹ค. + +1๋…„ ์ฐจ์—๋Š” ๋…๋ฆฝ์ ์ธ ๊ธฐ๋Šฅ ๋‹จ์œ„ ๊ฐœ๋ฐœ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ชจ๋“ˆ์— ์‹ ๊ทœ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ๋‹ค๋ฅธ ์‹œ์Šคํ…œ๊ณผ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ยท๊ตฌํ˜„ํ•˜๋Š” ์ž‘์—…์„ ๋งก๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ ๋ฆฌ๋ทฐ์— ์ ๊ทน ์ฐธ์—ฌํ•ด ํŒ€์˜ ํ’ˆ์งˆ ๊ธฐ์ค€์„ ์ตํžˆ๊ฒ ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ ์‚ฌ์šฉํ•œ Result ํŒจํ„ด์ด๋‚˜ ํŒŒ์ผ I/O์™€ DB ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„ ๋ถ„๋ฆฌ ๊ฐ™์€ ๊ฒ€์ฆ๋œ ์„ค๊ณ„ ๊ธฐ๋ฒ•์€ ํŒ€์— ๊ณต์œ ํ•˜๋ฉฐ ํ•จ๊ป˜ ๋ฐœ์ „์‹œํ‚ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +3๋…„ ์ฐจ์—๋Š” AI ๊ธฐ๋Šฅ ํ†ตํ•ฉ๊ณผ ๊ตฌ์กฐ ๊ฐœ์„ ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ˆ ํ†ต์—ฌ์ง€๋„์—์„œ GPT ํ˜ธ์ถœ ๋น„์šฉ์„ 70% ์ ˆ๊ฐํ•œ ๊ฒฝํ—˜์„ ์‚ด๋ ค, LIMS์˜ ํ’ˆ์งˆ ๋ฐ์ดํ„ฐ ๋ถ„์„์ด๋‚˜ SHE์˜ ์‚ฌ๊ณ  ์˜ˆ์ธก ๊ฐ™์€ ์˜์—ญ์—์„œ AI ๋„์ž…์„ ์ œ์•ˆํ•˜๊ณ  PoC๋ถ€ํ„ฐ ์šด์˜๊นŒ์ง€ ๋Œ๊ณ  ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์‹ ์ž…ยทํ›„๋ฐฐ ๊ฐœ๋ฐœ์ž์˜ ์˜จ๋ณด๋”ฉ์„ ๋•๊ณ , ๋ชจ๋“ˆ ๊ฐ„ ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๊ตฌ์กฐ ๊ฐœ์„ ์„ ์ฃผ๋„ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๋Œ€๊ทœ๋ชจ ์šด์˜ ํ™˜๊ฒฝ์—์„œ์˜ ์žฅ์•  ๋Œ€์‘ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ CloudSharp์—์„œ Recovery Worker์™€ CAS ๊ธฐ๋ฐ˜ ๋ฝ ํšŒ๋ณต ์ ˆ์ฐจ๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•˜๋ฉฐ ์šด์˜ ๊ฐ๊ฐ์„ ๋ณด์™„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋Š” ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์ด ๋ถ€์กฑํ•จ์„ ๋น ๋ฅด๊ฒŒ ๋ฉ”์›Œ๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์„ฑ์žฅ ๊ณผ์ • + +### ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋กœ + +์ €๋Š” "๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ"์—์„œ "์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ"๋กœ ์‹œ๊ฐ์ด ๋ฐ”๋€ ์‹œ์ ์„ ๊ฐœ๋ฐœ์ž ์ •์ฒด์„ฑ์˜ ์ถœ๋ฐœ์ ์ด๋ผ๊ณ  ๋ด…๋‹ˆ๋‹ค. + +์ฒ˜์Œ์—๋Š” ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋ฉด ๋์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Blazor WebAssembly์—์„œ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋˜ ์ค‘, ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ˜๋ณต ํ˜ธ์ถœํ•˜๋‹ค ์ฝœ๋ฐฑ ๋ˆ„์ˆ˜์™€ JS ๋ชจ๋“ˆ ์ค‘๋ณต ๋กœ๋”ฉ ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋•Œ ์ฒ˜์Œ์œผ๋กœ "์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ถ”์ถœํ•˜๋ฉด ์–ด๋–จ๊นŒ"๋ผ๋Š” ์งˆ๋ฌธ์„ ๋˜์กŒ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ tus ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜ ์—…๋กœ๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ TusBlazorClient๋ฅผ ๋งŒ๋“ค์–ด NuGet์— ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค(v1.0.1). ์ด ๊ณผ์ •์—์„œ Public API์™€ internal ๊ตฌํ˜„์„ ์—„๊ฒฉํžˆ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. IAsyncDisposable๋กœ JS ๋ชจ๋“ˆ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. Selenium ๊ธฐ๋ฐ˜ E2E ํ…Œ์ŠคํŠธ๋กœ ์—…๋กœ๋“œยท์žฌ๊ฐœยท์žฌ์‹œ๋„ ๋“ฑ 9๊ฐœ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์ž˜๋ชป ์“ธ ์ˆ˜ ์žˆ์„์ง€ ์˜ˆ์ƒํ•˜๊ณ  ๋ฐฉ์–ดํ•˜๋Š” ์ผ์€ ์ฒ˜์Œ์—๋Š” ๋ฒˆ๊ฑฐ๋กœ์› ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฒฐ๊ตญ ๊ทธ ์ž‘์—…์ด ์ฝ”๋“œ์˜ ์ˆ˜๋ช…์„ ๊ฒฐ์ •ํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. + +์ดํ›„ ์ง„ํ–‰ํ•œ CloudSharp ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ Clean Architecture๋กœ 12๊ฐœ ์—”ํ‹ฐํ‹ฐ์˜ ๋„๋ฉ”์ธ ๊ฒฝ๊ณ„๋ฅผ ๋ถ„๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. Finalize ์ค‘๋ณต ์‹คํ–‰์„ ๋ง‰๋Š” CAS ๊ธฐ๋ฐ˜ ๋ฝ, Quota ๊ฒฝ์Ÿ์„ ๋ง‰๋Š” FOR UPDATE ํŠธ๋žœ์žญ์…˜, ๋‹จ๋ช… 5๋ถ„ TTL ๋‹ค์šด๋กœ๋“œ ์„ธ์…˜ ๊ฐ™์€ ์„ค๊ณ„๋Š” ๋ชจ๋‘ "๋‚˜์ค‘์— ์šด์˜ํ•˜๋ฉด์„œ ํ›„ํšŒํ•˜์ง€ ์•Š์„ ์ฝ”๋“œ"๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์„ ํƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +์ง€๊ธˆ์˜ ์ €๋Š” "๊ธฐ๋Šฅ์„ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž"๋ณด๋‹ค "์˜ค๋ž˜ ์šด์˜ํ•ด๋„ ๋ฌด๋„ˆ์ง€์ง€ ์•Š๋Š” ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž"๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค. ๊ท€์‚ฌ์˜ ์ธ์žฌ์ƒ์ธ CreationยทCreditยทChallenge ์ค‘์—์„œ๋„ ์‹ ๋ขฐ(Credit)์— ๊ฐ€์žฅ ๊นŠ์ด ๊ณต๊ฐํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ์‚ฐ์—…์šฉ ์†”๋ฃจ์…˜์€ ํ•œ ๋ฒˆ ๋„์ž…๋˜๋ฉด ์ˆ˜๋…„๊ฐ„ ์šด์˜๋˜๋Š” ์‹œ์Šคํ…œ์ด๋ฉฐ, ๊ทธ ์•ˆ์—์„œ ์‹ ๋ขฐ๋Š” ๊ฒฐ๊ตญ ์ฝ”๋“œ์˜ ๊ตฌ์กฐ์™€ ๊ฒ€์ฆ๋œ ์„ค๊ณ„์—์„œ ๋‚˜์˜จ๋‹ค๊ณ  ๋ฏฟ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ์‚ฌ๊ณ ๋ฐฉ์‹์ด ์œ„์กด์˜ EHSยทLIMSยท์ถœ์ž…๊ด€๋ฆฌ ์†”๋ฃจ์…˜์ฒ˜๋Ÿผ ์žฅ๊ธฐ๊ฐ„ ์šด์˜๋˜๋Š” ์‹œ์Šคํ…œ์—์„œ ๊ฐ•์ ์ด ๋˜๋ฆฌ๋ผ ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/์ผ€์ด์—์Šค์•„์ด.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์ผ€์ด์—์Šค์•„์ด.md new file mode 100644 index 0000000..4c3c09c --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/์ผ€์ด์—์Šค์•„์ด.md @@ -0,0 +1,84 @@ +# ์ž๊ธฐ์†Œ๊ฐœ์„œ โ€” ใˆœ์ผ€์ด์—์Šค์•„์ด ์ง€์› + +> ์ž‘์„ฑ ๊ธฐ์ค€: IT ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ ์ปจ๋ฒค์…˜ (ํšŒ์‚ฌ ๋ถ„์„ + ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ, ๋‘๊ด„์‹, STAR ๊ตฌ์กฐ, ์ˆ˜์น˜ ๊ฒฐ๊ณผ ํฌํ•จ) + +--- + +## 1. ์ง€์›๋™๊ธฐ + +### ๋‹ท๋„ท๊ณผ AI๊ฐ€ ๋งŒ๋‚˜๋Š” ์ง€์  + +์ €๋Š” ๊ท€์‚ฌ์˜ MES/POP ์‹œ์Šคํ…œ์— AI๋ฅผ ์ ‘๋ชฉํ•˜๋ ค๋Š” ๋ฐฉํ–ฅ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” 1988๋…„๋ถ€ํ„ฐ 35๋…„๊ฐ„ .NET ๊ธฐ๋ฐ˜ MES/POPยทERP ์†”๋ฃจ์…˜์„ ์ž์ฒด ๊ฐœ๋ฐœํ•ด์˜จ ์ „๋ฌธ ๊ธฐ์—…์ž…๋‹ˆ๋‹ค. ์ตœ๊ทผ์—๋Š” AI ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์ž๋ฅผ ์ฑ„์šฉํ•˜๋ฉฐ ๊ณต์ • ๋ฐ์ดํ„ฐ ๋ถ„์„๊ณผ ์˜ˆ์ง€๋ณด์ „์œผ๋กœ ์˜์—ญ์„ ํ™•์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ MICOM PLC์™€ ๋ฌด์„  ์ปจ๋ฒ„ํ„ฐ๋ฅผ ์ง์ ‘ ๊ฐœ๋ฐœํ•ด ์†Œํ”„ํŠธ์›จ์–ด์™€ ํ†ตํ•ฉํ•˜๋Š” ๊ตฌ์กฐ๊ฐ€ ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ์—…๋ฌด ์‹œ์Šคํ…œ์ด ์•„๋‹ˆ๋ผ, ํ˜„์žฅ ์„ค๋น„๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ ๋ถ„์„๊นŒ์ง€ ์ „์ฒด๋ฅผ ์ฑ…์ž„์ง€๋Š” ์†”๋ฃจ์…˜์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +์ €๋Š” ASP.NET Core ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ €์žฅ ํ”Œ๋žซํผ์ธ CloudSharp๋ฅผ ์„ค๊ณ„ยท๊ตฌํ˜„ํ•˜๋ฉฐ .NET ํ™˜๊ฒฝ์—์„œ ์ธ์ฆ, ๋™์‹œ์„ฑ ์ œ์–ด, ์ธํ”„๋ผ ๊ตฌ์„ฑ๊นŒ์ง€ ์ง์ ‘ ์ฑ…์ž„์ง„ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ˆ ํ†ต์—ฌ์ง€๋„ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” GPT-5.2๋ฅผ ์‹ค์„œ๋น„์Šค์— ์ ์šฉํ•ด ํ† ํฐ์„ ์•ฝ 70% ์ ˆ๊ฐํ•˜๊ณ  Hallucination์„ ์ฐจ๋‹จํ•˜๋Š” ๋‹ค์ธต ๋ฐฉ์–ด ์ฒด๊ณ„๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. + +.NET ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ ์„ค๊ณ„์™€ AI ํ™œ์šฉ ๊ฒฝํ—˜์ด ๋™์‹œ์— ์š”๊ตฌ๋˜๋Š” ํ™˜๊ฒฝ์€ ํ”์น˜ ์•Š์Šต๋‹ˆ๋‹ค. ๊ท€์‚ฌ๊ฐ€ ์ถ”์ง„ํ•˜๋Š” MES/POP์˜ AI ๊ณ ๋„ํ™” ๋ฐฉํ–ฅ์ด ์ œ๊ฐ€ ์Œ“์•„์˜จ ๊ฒฝํ—˜๊ณผ ์ง์ ‘ ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•ด ์ง€์›์„ ๊ฒฐ์‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### ์›์ธ์„ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๋Š” ๊ฐœ๋ฐœ + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ์‹œ์Šคํ…œ ๋™์‹œ์„ฑ ๋ฌธ์ œ์˜ ์›์ธ์„ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๊ณ  ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +ASP.NET Core ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์ €์žฅ ํ”Œ๋žซํผ์ธ CloudSharp๋ฅผ ๊ฐœ๋ฐœํ•˜๋ฉฐ, ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ์˜ Finalize ๋‹จ๊ณ„์—์„œ ์ค‘๋ณต ์‹คํ–‰ ๊ฐ€๋Šฅ์„ฑ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. tusd hook๊ณผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ Worker ๋‘ ๊ฒฝ๋กœ์—์„œ ๊ฐ™์€ ์—…๋กœ๋“œ ์„ธ์…˜์ด ๋™์‹œ์— ์ฒ˜๋ฆฌ๋  ๊ฒฝ์šฐ, ๋™์ผํ•œ ํŒŒ์ผ์ด ๋‘ ๋ฒˆ ๋“ฑ๋ก๋  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. + +๋จผ์ € ๋™์‹œ ์š”์ฒญ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ถ„์„ํ•ด ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ๋ถ„์‚ฐ ๋ฝ(Redis Redlock ๋“ฑ)์„ ๋„์ž…ํ•˜๋ฉด ์ธํ”„๋ผ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฏ€๋กœ, ๊ธฐ์กด ์ƒํƒœ ๋จธ์‹ ์„ ๋ฝ์œผ๋กœ ์žฌํ™œ์šฉํ•˜๋Š” ๋ฐฉํ–ฅ์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ตฌ์ฒด์ ์œผ๋กœ๋Š” PostgreSQL์˜ CAS(Compare-And-Swap) ํŒจํ„ด์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. `UPDATE upload_session SET status='FINALIZING' WHERE status='UPLOADING'` ์ฟผ๋ฆฌ๋กœ ๋‹จ์ผ ํŠธ๋žœ์žญ์…˜ ์ ์œ ๋ฅผ ๊ตฌํ˜„ํ–ˆ๊ณ , ํŒŒ์ผ I/O์™€ DB ํŠธ๋žœ์žญ์…˜์„ ๋ถ„๋ฆฌํ•ด ๋А๋ฆฐ ์ž‘์—…์ด ๋ฝ์„ ์žก์ง€ ์•Š๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ 10๋ถ„ ์ด์ƒ FINALIZING ์ƒํƒœ์— ๋จธ๋ฌธ ์„ธ์…˜์„ ์ž๋™ ๋ณต๊ตฌํ•˜๋Š” Recovery Worker๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ ๋ณ„๋„ ๋ถ„์‚ฐ ๋ฝ ์ธํ”„๋ผ ์—†์ด SQL ํ•œ ์ค„๋กœ ์ค‘๋ณต ์‹คํ–‰์„ ์ฐจ๋‹จํ–ˆ๊ณ , DB UNIQUE ์ œ์•ฝ๊ณผ ๊ฒฐํ•ฉํ•ด ์ด์ค‘ ์•ˆ์ „์žฅ์น˜๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ Quota ๋™์‹œ ์š”์ฒญ ๋ฌธ์ œ์—๋„ `SELECT FOR UPDATE`๋ฅผ ์ ์šฉํ•ด ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์„ ํ†ตํ•ด ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋„˜์–ด, ๋™์‹œ์„ฑ๊ณผ ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ๋ฅผ ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ์‹์„ ์ตํ˜”์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ๋™์ผํ•œ ์ ‘๊ทผ์œผ๋กœ MES/POP ์‹œ์Šคํ…œ์˜ ์‹ค์‹œ๊ฐ„ ๊ณต์ • ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์•ˆ์ •์„ฑ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ํ˜„์žฅ๊ณผ AI๋ฅผ ์ž‡๋Š” ๊ฐœ๋ฐœ์ž + +์ž…์‚ฌ ํ›„์—๋Š” ๊ท€์‚ฌ์˜ ์Šค๋งˆํŠธํŒฉํ† ๋ฆฌ AI ๊ณ ๋„ํ™” ๋ฐฉํ–ฅ ์•ˆ์—์„œ .NET ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ๊ณผ AI๋ฅผ ์ž‡๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” MES/POP ์‹œ์Šคํ…œ์— AI๋ฅผ ์ ‘๋ชฉํ•ด ๊ณต์ • ๋ฐ์ดํ„ฐ ๋ถ„์„๊ณผ ์˜ˆ์ง€๋ณด์ „์œผ๋กœ ์˜์—ญ์„ ํ™•์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ๋ฆ„ ์•ˆ์—์„œ ๋‹จ์ˆœ ๊ธฐ๋Šฅ ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ, ํ˜„์žฅ ๋ฐ์ดํ„ฐ์˜ ์‹ ๋ขฐ์„ฑ๊ณผ ๋ถ„์„ ๊ฒฐ๊ณผ์˜ ์ •ํ™•์„ฑ์„ ํ•จ๊ป˜ ์ฑ…์ž„์ง€๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ์—๋Š” ์—…์ข…๋ณ„ MES/POP ํŒจํ‚ค์ง€ ๊ตฌ์กฐ์™€ PLC ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ž๋™์ฐจ๋ถ€ํ’ˆ, ํ”„๋ ˆ์Šค/๊ธˆํ˜•, ์ธ์‡„ ๋“ฑ ์—…์ข…๋งˆ๋‹ค ๊ณต์ •๊ณผ ๋ฐ์ดํ„ฐ ํŠน์„ฑ์ด ๋‹ฌ๋ผ, ์ฝ”๋“œ๋ฒ ์ด์Šค์™€ ํ˜„์žฅ ๋„๋ฉ”์ธ์„ ํ•จ๊ป˜ ํ•™์Šตํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค. + +1๋…„ ์•ˆ์—๋Š” .NET ๊ธฐ๋ฐ˜ ๋ชจ๋“ˆ์„ ๋‹จ๋…์œผ๋กœ ๋‹ด๋‹นํ•˜๊ณ , API ์—ฐ๋™๊ณผ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๊ฐœ์„ ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ ์Œ“์€ ๋™์‹œ์„ฑ ์ œ์–ด ๊ฒฝํ—˜๊ณผ ์ˆ ํ†ต์—ฌ์ง€๋„์—์„œ ๋‹ค์ง„ ์™ธ๋ถ€ API ์•ˆ์ •ํ™” ๊ฒฝํ—˜์ด ๋ฐ”๋กœ ํ™œ์šฉ๋  ์˜์—ญ์ž…๋‹ˆ๋‹ค. + +3๋…„ ์•ˆ์—๋Š” MES/POP ์‹œ์Šคํ…œ์— AI ๋ถ„์„์„ ์•ˆ์ •์ ์œผ๋กœ ํ†ตํ•ฉํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์ œ์•ˆํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. GPT ๊ธฐ๋ฐ˜ ์ถ”์ฒœ ์—”์ง„์—์„œ ํ† ํฐ ๋น„์šฉ๊ณผ Hallucination์„ ๋™์‹œ์— ์žก์•˜๋˜ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ, ์ œ์กฐ ๋„๋ฉ”์ธ AI์—์„œ๋„ ๋น„์šฉยท์ •ํ™•๋„ยท์šด์˜ ์•ˆ์ •์„ฑ์„ ํ•จ๊ป˜ ๊ณ ๋ คํ•œ ์„ค๊ณ„๋ฅผ ๋งŒ๋“ค์–ด๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. + +PLC์™€ ํ•˜๋“œ์›จ์–ด ์—ฐ๋™ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, MICOM PLC์™€ ๋ฌด์„  ์ปจ๋ฒ„ํ„ฐ ๊ด€๋ จ ์ž๋ฃŒ๋ฅผ ๋ฏธ๋ฆฌ ํ•™์Šตํ•˜๋ฉฐ ์‚ฌ๋‚ด OJT๋ฅผ ํ†ตํ•ด ๋น ๋ฅด๊ฒŒ ๋ณด์™„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์žฅ๊ธฐ์ ์œผ๋กœ๋Š” ํ˜„์žฅ๊ณผ AI๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฐœ๋ฐœ ์‚ฌ๋ก€๋ฅผ ๋งŒ๋“ค์–ด, ๊ท€์‚ฌ์˜ ์Šค๋งˆํŠธํŒฉํ† ๋ฆฌ ์†”๋ฃจ์…˜์ด ํ•œ ๋‹จ๊ณ„ ํ™•์žฅ๋˜๋Š” ๋ฐ ๋ณดํƒฌ์ด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • + +### ๊ตฌํ˜„์—์„œ ๊ตฌ์กฐ๋กœ + +์ €๋Š” ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋„˜์–ด ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ๊ธฐ๋Šฅ์„ ๋น ๋ฅด๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์™€ ์ž˜ ๋งŒ๋“  ์ฝ”๋“œ๋Š” ๋‹ค๋ฅด๋‹ค๋Š” ์ ์„ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ˆ ํ†ต์—ฌ์ง€๋„ ํ”„๋กœ์ ํŠธ์—์„œ GPT-5.2๋ฅผ ์‹ค์„œ๋น„์Šค์— ์ ์šฉํ–ˆ์„ ๋•Œ, ๋‹จ์ˆœํžˆ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ˆ˜์ค€์œผ๋กœ๋Š” ์šด์˜์ด ๋ถˆ๊ฐ€๋Šฅํ–ˆ์Šต๋‹ˆ๋‹ค. 200๊ฐœ ํ›„๋ณด๋ฅผ ํ•œ ๋ฒˆ์— ๋ณด๋‚ด๋ฉด ํ† ํฐ์ด 16,000๊ฐœ๋ฅผ ๋„˜์—ˆ๊ณ , GPT๋Š” ์ž…๋ ฅ์— ์—†๋Š” ์ˆ ์ง‘์„ ๋งŒ๋“ค์–ด๋‚ด๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ’€๊ธฐ ์œ„ํ•ด 2๋‹จ๊ณ„ Cascade Ranking ๊ตฌ์กฐ๋ฅผ ๋„์ž…ํ–ˆ๊ณ , ์‘๋‹ต์„ ์ •๊ทœ์‹์œผ๋กœ ๊ฒ€์ฆํ•ด ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ID๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋‹ค์ธต ๋ฐฉ์–ด ์ฒด๊ณ„๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ณผ์ •์—์„œ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ํ†ต์‹ ํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์‹คํŒจ์™€ ๋น„์ •์ƒ ์‘๋‹ต์„ ์ „์ œ๋กœ ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์›์น™์„ ์ฒด๋“ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ดํ›„ CloudSharp ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ Clean Architecture, Result ๊ธฐ๋ฐ˜ ์—๋Ÿฌ ์ฒ˜๋ฆฌ, CAS ๋™์‹œ์„ฑ ์ œ์–ด๋ฅผ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. TusBlazorClient๋ผ๋Š” .NET ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋‹จ๋…์œผ๋กœ ์„ค๊ณ„ํ•ด NuGet์— ๋ฐฐํฌํ•˜๋ฉด์„œ, ์™ธ๋ถ€ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์šฉํ•  API์˜ ์ฑ…์ž„ ๋ถ„๋ฆฌ์™€ ํ™•์žฅ์„ฑ๋„ ์ง์ ‘ ๊ณ ๋ฏผํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ๊ฒฝํ—˜์„ ๊ฑฐ์น˜๋ฉฐ ๋‹จ์ˆœ ๊ตฌํ˜„๋ณด๋‹ค ์›์ธ ๋ถ„์„๊ณผ ๊ตฌ์กฐ ์„ค๊ณ„๋ฅผ ์šฐ์„ ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ์ด ์ž์„ธ๋กœ ๊ท€์‚ฌ ์†”๋ฃจ์…˜์˜ ํ’ˆ์งˆ๊ณผ ์•ˆ์ •์„ฑ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## ์ž‘์„ฑ ์ปจ๋ฒค์…˜ ์ ๊ฒ€ + +| ํ•ญ๋ชฉ | ์ ๊ฒ€ ๊ฒฐ๊ณผ | +|---|---| +| ํšŒ์‚ฌ ์—ฐ๊ฒฐ | MES/POP, AI ์ ‘๋ชฉ, MICOM PLC, 35๋…„ ์—ญ์‚ฌ ๋“ฑ ํšŒ์‚ฌ ์ •๋ณด ์ง์ ‘ ์ธ์šฉ | +| ๊ฒฝํ—˜ ์ฆ๋ช… | CloudSharp(CAS), ์ˆ ํ†ต์—ฌ์ง€๋„(GPT 70% ์ ˆ๊ฐ), TusBlazorClient(NuGet) ์‚ฌ๋ก€ ์‚ฌ์šฉ | +| ์ˆ˜์น˜ ๊ฒฐ๊ณผ | ํ† ํฐ 70% ์ ˆ๊ฐ, 16,000 ํ† ํฐ, 10๋ถ„ ์ž๋™ ๋ณต๊ตฌ, 35๋…„ | +| ์ž…์‚ฌ ํ›„ ๊ธฐ์—ฌ | ์ดˆ๊ธฐ โ†’ 1๋…„ โ†’ 3๋…„ ํƒ€์ž„๋ผ์ธ์œผ๋กœ ๊ตฌ์„ฑ, ๋ณด์™„์ (PLC) ๋ช…์‹œ | +| ๋‘๊ด„์‹ | ๋ชจ๋“  ๋ฌธํ•ญ ์ฒซ ๋ฌธ์žฅ์— ๊ฒฐ๋ก  ์œ„์น˜ | +| ๊ธˆ์ง€ ํ‘œํ˜„ | "์—ด์ •", "์ตœ์„ ", "๋ˆ„๊ตฌ๋ณด๋‹ค", "์ธ์žฌ" ๋ฏธ์‚ฌ์šฉ | +| ๋ฌธ์žฅ ๊ธธ์ด | 60์ž ๋‚ด์™ธ ์œ ์ง€, 80์ž ์ดˆ๊ณผ ๋ฌธ์žฅ ์—†์Œ | +| ์†Œ์ œ๋ชฉ | 15์ž ๋‚ด์™ธ, ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€ ๋ฐ˜์˜ | \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/ํฌ๋ ˆ์…ˆ.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/ํฌ๋ ˆ์…ˆ.md new file mode 100644 index 0000000..c0bcd62 --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/ํฌ๋ ˆ์…ˆ.md @@ -0,0 +1,77 @@ +# ใˆœํฌ๋ ˆ์…ˆ ์ž๊ธฐ์†Œ๊ฐœ์„œ โ€” S/W ๊ฐœ๋ฐœ์ž(์ œ์–ด) + +> ์ปจ๋ฒค์…˜ ๊ฐ€์ด๋“œ(๋‘๊ด„์‹ / ํšŒ์‚ฌ ๋ถ„์„ + ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ / STAR / 60์ž ๋‚ด์™ธ ๋ฌธ์žฅ / ์ถ”์ƒ์–ด ๊ธˆ์ง€)์— ๋”ฐ๋ผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. +> ์ฑ„์šฉ๊ณต๊ณ  ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ: **C#, Visual Studio, ๊ฒ€์‚ฌ์žฅ๋น„ ์ œ์–ด, MMI** +> ํ™œ์šฉ ๊ฒฝํ—˜: **TusBlazorClient(C# ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌยทNuGet)**, **CloudSharp(ASP.NET Core ํŒŒ์ผ ์„œ๋น„์ŠคยทCAS ๋™์‹œ์„ฑ ์ œ์–ด)** + +--- + +## 1. ์ง€์›๋™๊ธฐ โ€” ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ๋งŒ๋“œ๋Š” ์ฐจ์ด + +์ €๋Š” ํฌ๋ ˆ์…ˆ์ด ํ•˜๋“œ์›จ์–ด ์ œ์–ด๊ธฐ๋ฅผ ์†Œํ”„ํŠธ์›จ์–ด ๋ชจ์…˜ ์ œ์–ด๋กœ ์ „ํ™˜ํ•˜๋ฉฐ IT์™€ OT๋ฅผ ์œตํ•ฉํ•ด ๊ฐ€๋Š” ๋ฐฉํ–ฅ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +ํฌ๋ ˆ์…ˆ์€ 2D/3D ๋น„์ „, AI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, C# ๊ธฐ๋ฐ˜ ์ œ์–ด ํ”„๋กœ๊ทธ๋žจ์„ ๊ฒฐํ•ฉํ•ด ๊ฒ€์‚ฌ์žฅ๋น„๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ ๊ฐ๋ณ„ ๊ณต์ •์— ๋งž๋Š” ์žฅ๋น„๋ฅผ ํ”Œ๋žซํผ ๊ธฐ์ˆ  ์œ„์—์„œ ๋น ๋ฅด๊ฒŒ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•œ๋‹ค๋Š” ์ ์ด ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. WMX ์†Œํ”„ํŠธ์›จ์–ด ๋ชจ์…˜ ์ œ์–ด ๋„์ž… ์‚ฌ๋ก€์—์„œ๋Š” ๊ด€๋ฆฌ ์ธ๋ ฅ์„ 12๋ช…์—์„œ 1๋ช…์œผ๋กœ ์ค„์ด๊ณ  ์žฅ๋น„ ๋ฉด์ ์„ 1/20๋กœ ์ถ•์†Œํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”์Šต๋‹ˆ๋‹ค. ๊ฒ€์‚ฌ์žฅ๋น„์˜ ๊ฒฝ์Ÿ๋ ฅ์ด ๊ฒฐ๊ตญ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด์˜ ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์—์„œ ๊ฒฐ์ •๋œ๋‹ค๊ณ  ํŒ๋‹จํ•œ ๊ณ„๊ธฐ์˜€์Šต๋‹ˆ๋‹ค. + +์ €๋Š” C# ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ์„ ์ง์ ‘ ์„ค๊ณ„ํ•˜๊ณ  ์šด์˜ํ•ด์™”์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ณผ์ •์—์„œ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ์•ˆ์ •์  ์—ฐ๋™, ์žฅ๊ธฐ ์‹คํ–‰ ์ž‘์—…์˜ ์‹ ๋ขฐ์„ฑ ํ™•๋ณด๋ฅผ ํ•ต์‹ฌ ๊ณผ์ œ๋กœ ๋‹ค๋ค˜์Šต๋‹ˆ๋‹ค. Blazor WebAssembly์šฉ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋Š” .NET๊ณผ JavaScript ๋ชจ๋“ˆ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ–ˆ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ NuGet์— ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค. ASP.NET Core ๊ธฐ๋ฐ˜ ํŒŒ์ผ ์„œ๋น„์Šค์—์„œ๋Š” ์™ธ๋ถ€ ์—…๋กœ๋“œ ์„œ๋ฒ„์™€ ๋™๊ธฐํ™”๋˜๋Š” ์ƒํƒœ ๋จธ์‹ ์„ CAS ํŒจํ„ด์œผ๋กœ ์„ค๊ณ„ํ•ด ์ค‘๋ณต ์‹คํ–‰์„ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด๋Ÿฐ ๊ฒฝํ—˜์€ ์„ผ์„œยท๋ชจ์…˜ยทํ›„์† ๊ณต์ • ์žฅ๋น„๋ฅผ ์ž‡๋Š” ๊ฒ€์‚ฌ์žฅ๋น„ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด์—์„œ ๊ทธ๋Œ€๋กœ ์“ฐ์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์œตํ•ฉยท์ •์—ดยท์‹ ๋ขฐ๋ฅผ ํ•ต์‹ฌ ๊ฐ€์น˜๋กœ ๋‘๋Š” ํฌ๋ ˆ์…ˆ์˜ ๋ฐฉํ–ฅ๊ณผ ์ œ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  โ€” ์™ธ๋ถ€ ์‹œ์Šคํ…œ์˜ ์ƒํƒœ๋ฅผ ๋๊นŒ์ง€ ์ฑ…์ž„์ง€๋‹ค + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ฒฐ๋œ ์ž‘์—…์˜ ์ƒํƒœ๋ฅผ ๋๊นŒ์ง€ ์ฑ…์ž„์ง€๊ณ  ์•ˆ์ •ํ™”ํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. + +ASP.NET Core ๊ธฐ๋ฐ˜ ์…€ํ”„ํ˜ธ์ŠคํŒ… ํŒŒ์ผ ์„œ๋น„์Šค CloudSharp๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ฐ™์€ ์—…๋กœ๋“œ ์„ธ์…˜์„ ๋™์‹œ์— ์™„๋ฃŒ ์ฒ˜๋ฆฌํ•˜๋ฉด, ํŒŒ์ผ์ด ์ค‘๋ณต ๋“ฑ๋ก๋˜๊ฑฐ๋‚˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๊ธ‹๋‚  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ์˜€์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์—…๋กœ๋“œ ์„œ๋ฒ„ tusd๊ฐ€ ๋ณด๋‚ด๋Š” hook๊ณผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ worker๊ฐ€ ๊ฐ™์€ ์„ธ์…˜์„ ๋™์‹œ์— ๊ฑด๋“œ๋ฆด ์ˆ˜ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. + +๋ถ„์‚ฐ ๋ฝ ์ธํ”„๋ผ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, ์ €๋Š” PostgreSQL์˜ ๋‹จ์ผ UPDATE ๋ฌธ์„ ๋ฝ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” CAS ํŒจํ„ด์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. `UPDATE upload_session SET status='FINALIZING' WHERE status='UPLOADING'` ์ฟผ๋ฆฌ์—์„œ ์˜ํ–ฅ๋ฐ›์€ ํ–‰์ด 1์ด๋ฉด ์ ์œ  ์„ฑ๊ณต, 0์ด๋ฉด ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฒ˜๋ฆฌ ์ค‘์ด๋ผ๊ณ  ํŒ๋‹จํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๋А๋ฆฐ ํŒŒ์ผ I/O์™€ ์งง์€ DB ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฝ๊ณ„๋ฅผ ๋ถ„๋ฆฌํ•ด ๋ฝ ์ ์œ  ์‹œ๊ฐ„์„ ์ตœ์†Œํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. 10๋ถ„ ์ด์ƒ ์ ์œ  ์ƒํƒœ์— ๋จธ๋ฌด๋Š” ์„ธ์…˜์€ ๋ณ„๋„ Recovery Worker๊ฐ€ ์ž๋™์œผ๋กœ ๋ณต๊ตฌํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ ์™ธ๋ถ€ ๋ฝ ์ธํ”„๋ผ ์—†์ด ๋™์‹œ finalize ์ค‘๋ณต ์‹คํ–‰์„ ์ฐจ๋‹จํ–ˆ๊ณ , ์žฅ์•  ์‹œ์—๋„ ์šด์˜์ž ๊ฐœ์ž… ์—†์ด ์‹œ์Šคํ…œ์ด ์ •์ƒ ์ƒํƒœ๋กœ ๋Œ์•„์˜ค๋„๋ก ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. + +๊ฒ€์‚ฌ์žฅ๋น„ ์ œ์–ด ํ”„๋กœ๊ทธ๋žจ์€ ์„ผ์„œ, ๋ชจ์…˜ ์ œ์–ด๊ธฐ, ํ›„์† ๊ณต์ • ์žฅ๋น„์™€ ๋™์‹œ์— ํ†ต์‹ ํ•˜๋Š” ์˜์—ญ์ž…๋‹ˆ๋‹ค. ์–ด๋А ํ•œ ๋‹จ๊ณ„๋ผ๋„ ์–ด๊ธ‹๋‚˜๋ฉด ๊ณต์ • ์ „์ฒด๊ฐ€ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค. ์ด ๊ฒฝํ—˜์—์„œ ์ตํžŒ ์ƒํƒœ ๋จธ์‹  ์„ค๊ณ„์™€ ๋™์‹œ์„ฑ ์ œ์–ด ๋ฐฉ์‹์„, ํฌ๋ ˆ์…ˆ์˜ ๊ฒ€์‚ฌ์žฅ๋น„ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด์—์„œ๋„ ๊ฐ™์€ ์›์น™์œผ๋กœ ์ ์šฉํ•ด ์žฅ๋น„ ์•ˆ์ •์„ฑ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ โ€” ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ•จ๊ป˜ + +์ž…์‚ฌ ํ›„์—๋Š” ๊ฒ€์‚ฌ์žฅ๋น„์˜ ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ•จ๊ป˜ ์ฑ…์ž„์ง€๋Š” ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +ํฌ๋ ˆ์…ˆ์€ ํ•œ ๋Œ€์˜ ์žฅ๋น„๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๊ทธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 2D/3D ๋น„์ „, Dual-Layer AI Filtering, MLOps ์„œ๋ฒ„, Inline ์ž๋™ํ™”๋ฅผ ํ•˜๋‚˜์˜ ํ”Œ๋žซํผ์œผ๋กœ ํ†ตํ•ฉํ•ด ๊ฐ€๋Š” ๋ฐฉํ–ฅ์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํ™˜๊ฒฝ์—์„œ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด๋Š” ์„ผ์„œ, AI ๋ชจ๋“ˆ, ํ›„์† ๊ณต์ • ์žฅ๋น„๋ฅผ ์ž‡๋Š” ์ค‘์‹ฌ์ถ•์ด ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. + +์ž…์‚ฌ ์ดˆ๊ธฐ 6๊ฐœ์›” ๋™์•ˆ์€ ๊ธฐ์กด ์žฅ๋น„์˜ C# ์ œ์–ด ์ฝ”๋“œ์™€ MMI ๊ตฌ์กฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ดํ•ดํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์šด์˜๋˜๋Š” ๊ฒ€์‚ฌ ๊ณต์ •์˜ ํ๋ฆ„์„ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์žฅ๋น„ ์‹œํ€€์Šค, ํ†ต์‹  ํ”„๋กœํ† ์ฝœ, ์‚ฌ์šฉ์ž ์šด์˜ ํŒจํ„ด์„ ์ตํžˆ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ž‘์€ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ๋‹จ๋…์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉฐ ๋„๋ฉ”์ธ ๊ฐ๊ฐ์„ ์Œ“๊ฒ ์Šต๋‹ˆ๋‹ค. + +1๋…„ ์ฐจ์—๋Š” ์‹ ๊ทœ ๊ฒ€์‚ฌ์žฅ๋น„ ์ œ์–ด ๋ชจ๋“ˆ์„ ๋‹จ๋…์œผ๋กœ ๋งก์•„ ์„ค๊ณ„๋ถ€ํ„ฐ ํ˜„์žฅ ์ ์šฉ๊นŒ์ง€ ์ฑ…์ž„์ง€๋Š” ๋‹จ๊ณ„๋กœ ์˜ฌ๋ผ๊ฐ€๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์‹œ์Šคํ…œ๊ณผ์˜ ํ†ต์‹  ์•ˆ์ •ํ™”, ๋น„์ •์ƒ ์ƒํ™ฉ์—์„œ์˜ ๋ณต๊ตฌ ์‹œํ€€์Šค ์„ค๊ณ„, MMI ์‚ฌ์šฉ์„ฑ ๊ฐœ์„  ์˜์—ญ์—์„œ ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค๊ฒ ์Šต๋‹ˆ๋‹ค. + +3๋…„ ์ฐจ์—๋Š” ํ”Œ๋žซํผ ๊ธฐ์ˆ ์˜ ํ•œ ์ถ•์„ ๋‹ด๋‹นํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์žฅ๋น„์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ์ œ์–ด ํŒจํ„ด์„ ๊ณตํ†ต ๋ชจ๋“ˆ๋กœ ์ •๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. AI ๊ฒ€์‚ฌ ๊ฒฐ๊ณผ, ๋ชจ์…˜ ์ œ์–ด, Inline ์ž๋™ํ™” ์‹œํ€€์Šค๊ฐ€ ํ•˜๋‚˜์˜ ํ๋ฆ„์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก ๊ตฌ์กฐ๋ฅผ ๋‹ค๋“ฌ๋Š” ์ผ์— ๊ธฐ์—ฌํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. + +๋ฐ˜๋„์ฒด ๊ฒ€์‚ฌ ๊ณต์ •๊ณผ ๋Œ€๊ทœ๋ชจ ์žฅ๋น„ ์šด์˜ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ C# ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ASP.NET Core ๋ฐฑ์—”๋“œ๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•˜๊ณ  NuGet๊ณผ Docker ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์–ด, ์ƒˆ๋กœ์šด ๋„๋ฉ”์ธ์—๋„ ๋น ๋ฅด๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. + +--- + +## 4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • โ€” ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ์—์„œ ๋๋‚˜์ง€ ์•Š๊ฒŒ + +์ €๋Š” ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ๋‹จ๊ณ„์—์„œ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ , ๊ทธ ์ฝ”๋“œ๊ฐ€ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์–ด๋–ป๊ฒŒ ๊ฒฌ๋””๋Š”์ง€๋ฅผ ๋๊นŒ์ง€ ํ™•์ธํ•˜๋ ค๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ๋ณธ๊ฒฉ์ ์œผ๋กœ ํ•œ ๊ฒƒ์€ Blazor WebAssembly ํ™˜๊ฒฝ์—์„œ ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค๋ฉด์„œ์˜€์Šต๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์ด ๋™์ž‘ํ•˜๋Š” ๋ฐ๊นŒ์ง€๋Š” ์–ด๋ ต์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋„คํŠธ์›Œํฌ๊ฐ€ ๋Š๊ธฐ๊ฑฐ๋‚˜ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋‹ซํ˜”์„ ๋•Œ ์ด์–ด ์˜ฌ๋ฆฌ๋Š” ์žฌ๊ฐœ ๋™์ž‘, JavaScript ๋ชจ๋“ˆ๊ณผ .NET ๊ฐ์ฒด ์‚ฌ์ด์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ์ผ์€ ์ „ํ˜€ ๋‹ค๋ฅธ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค. + +์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ NuGet์— ๋ฐฐํฌํ•˜๊ณ  ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ ๋งŒ๋“ค๋ฉด์„œ ํ•œ ๊ฐ€์ง€๋ฅผ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. "๋™์ž‘ํ•œ๋‹ค"์™€ "๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์•ˆ์ „ํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋‹ค" ์‚ฌ์ด์—๋Š” ํฐ ๊ฑฐ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋’ค๋กœ๋Š” ์–ด๋–ค ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋”๋ผ๋„ ์ƒํƒœ ๋จธ์‹ ๊ณผ ์‹คํŒจ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋จผ์ € ์ •๋ฆฌํ•˜๋Š” ์Šต๊ด€์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. + +์ดํ›„ ์…€ํ”„ํ˜ธ์ŠคํŒ… ํŒŒ์ผ ์„œ๋น„์Šค๋ฅผ ์ง์ ‘ ์„ค๊ณ„ํ•˜๋ฉด์„œ ์ด ๊ด€์ ์„ ํ•œ ๋‹จ๊ณ„ ํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์™ธ๋ถ€ ์—…๋กœ๋“œ ์„œ๋ฒ„, DB, Redis, ํŒŒ์ผ ์‹œ์Šคํ…œ์ด ๋™์‹œ์— ์›€์ง์ด๋Š” ํ™˜๊ฒฝ์—์„œ ์–ด๋А ํ•œ ๊ณณ์ด ์‹คํŒจํ•ด๋„ ์ „์ฒด ์ •ํ•ฉ์„ฑ์ด ๊นจ์ง€์ง€ ์•Š๋„๋ก ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์™€ ๋ณต๊ตฌ ๋กœ์ง์„ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ง€๊ธˆ์˜ ์ €๋Š” ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋งˆ์ฃผํ•  ๋•Œ "์ด ๋™์ž‘์ด ์‹คํŒจํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ณต๊ตฌ๋˜๋Š”๊ฐ€"๋ฅผ ๊ฐ€์žฅ ๋จผ์ € ๊ณ ๋ฏผํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. ๊ฒ€์‚ฌ์žฅ๋น„ ์ œ์–ด ์†Œํ”„ํŠธ์›จ์–ด์ฒ˜๋Ÿผ ํ•œ ๋ฒˆ์˜ ์˜ค์ž‘๋™์ด ๊ณต์ • ์ „์ฒด์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ์˜์—ญ์—์„œ, ์ด ์‚ฌ๊ณ ๋ฐฉ์‹์ด ๊ฐ€์žฅ ์ž˜ ์“ฐ์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. + +--- + +### ์ž‘์„ฑ ์‹œ ๋ฐ˜์˜ํ•œ ์ปจ๋ฒค์…˜ ์š”์•ฝ + +| ํ•ญ๋ชฉ | ์ ์šฉ ๋ฐฉ์‹ | +|---|---| +| **๋‘๊ด„์‹** | ๋ชจ๋“  ๋ฌธํ•ญ ์ฒซ ๋ฌธ์žฅ์—์„œ ๊ฒฐ๋ก  ์ œ์‹œ | +| **ํšŒ์‚ฌ ์—ฐ๊ฒฐ** | WMX ๋„์ž… ์‚ฌ๋ก€, Dual-Layer AI Filtering, MLOps, Inline ์ž๋™ํ™” ๋“ฑ ํฌ๋ ˆ์…ˆ ๊ณ ์œ  ํ‚ค์›Œ๋“œ ์ธ์šฉ | +| **STAR ๊ตฌ์กฐ** | ๊ฐ•์  ๋ฌธํ•ญ์„ CloudSharp CAS ์‚ฌ๋ก€๋กœ Sโ†’Tโ†’Aโ†’Rโ†’์ž…์‚ฌ ํ›„ ์—ฐ๊ฒฐ ์ˆœ์œผ๋กœ ์ „๊ฐœ | +| **์ˆ˜์น˜ ๊ฒฐ๊ณผ** | ์ธ๋ ฅ 12โ†’1๋ช…, ๋ฉด์  1/20, ์ ์œ  ์‹œ๊ฐ„ 10๋ถ„ ์ž„๊ณ„ ๋“ฑ ๊ตฌ์ฒด ์ˆ˜์น˜ ์‚ฌ์šฉ | +| **๊ธˆ์ง€ ํ‘œํ˜„ ํšŒํ”ผ** | "์—ด์ •", "์ตœ์„ ", "์ธ์žฌ", "๋ˆ„๊ตฌ๋ณด๋‹ค" ๋ฏธ์‚ฌ์šฉ | +| **์‚ฌ๋ก€ ๋ถ„๋ฆฌ** | ์ง€์›๋™๊ธฐยท๊ฐ•์ ยท์ž๊ธฐ์†Œ๊ฐœ์— ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ดˆ์ (์—ฐ๊ฒฐ์„ฑ / ๋™์‹œ์„ฑ ์ œ์–ด / ์„ฑ์žฅ ์ „ํ™˜์ ) ๋ฐฐ์น˜ | +| **์†Œ์ œ๋ชฉ** | ๋ฌธํ•ญ๋ช… ๋ฐ˜๋ณต ์—†์ด ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€๋กœ ์ž‘์„ฑ | + +์ถ”๊ฐ€๋กœ ๋‹ค๋ฅธ ๋ฌธํ•ญ(์˜ˆ: ํ˜‘์—… ๊ฒฝํ—˜, ์‹คํŒจ ๊ฒฝํ—˜, 1๋ถ„ ์ž๊ธฐ์†Œ๊ฐœ)์ด ํ•„์š”ํ•˜๋ฉด ๊ฐ™์€ ์ปจ๋ฒค์…˜์œผ๋กœ ์ด์–ด์„œ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/์ž๊ธฐ์†Œ๊ฐœ์„œ/ํŽ˜์ดํƒ€๋žฉ.md b/์ž๊ธฐ์†Œ๊ฐœ์„œ/ํŽ˜์ดํƒ€๋žฉ.md new file mode 100644 index 0000000..b833487 --- /dev/null +++ b/์ž๊ธฐ์†Œ๊ฐœ์„œ/ํŽ˜์ดํƒ€๋žฉ.md @@ -0,0 +1,55 @@ +# ์ž๊ธฐ์†Œ๊ฐœ์„œ + +> ใˆœํŽ˜์ดํƒ€๋žฉ ยท C# WPF ์‘์šฉํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ์ž ์ง€์› + +--- + +## 1. ์ง€์›๋™๊ธฐ โ€” Windows ํ™˜๊ฒฝ ์œ„์˜ ์•ˆ์ •์„ฑ + +์ €๋Š” ๊ท€์‚ฌ์˜ ํŒจ์Šค์˜ค๋”๊ฐ€ ํ’€์–ด๋‚ด๊ณ  ์žˆ๋Š” **"Windows POS์™€ ๋ชจ๋ฐ”์ผ ์„œ๋น„์Šค๋ฅผ ์ž‡๋Š”๋‹ค"**๋Š” ๋ฌธ์ œ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ์˜ ์ฑ„์šฉ ์ •๋ณด์™€ ๊ธฐ์ˆ  ๊ธ€์—์„œ ์ธ์ƒ ๊นŠ์—ˆ๋˜ ์ง€์ ์€ ๋‘ ๊ฐ€์ง€์˜€์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” ์นดํŽ˜ POS์˜ 99%๊ฐ€ Windows ๊ธฐ๋ฐ˜์ด๋ผ๋Š” ํ™˜๊ฒฝ ์œ„์—์„œ ๊ตฌํ˜• PC๋ถ€ํ„ฐ ์ตœ์‹  ํ‚ค์˜ค์Šคํฌ๊นŒ์ง€ ๋ชจ๋‘ ์ง€์›ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์˜ค์ „ ํ”ผํฌ์— ํ•˜๋ฃจ ํŠธ๋ž˜ํ”ฝ์˜ 33.3%๊ฐ€ ๋ชฐ๋ฆฌ๋Š” ๊ฐ€์šด๋ฐ, ์ ์ฃผ PC๊ฐ€ ์ดˆ๋‹น ์ˆ˜๋ฐฑ ๊ฑด์˜ ์ฃผ๋ฌธ์„ ๋ˆ„๋ฝ ์—†์ด ๋ฐ›์•„์•ผ ํ•œ๋‹ค๋Š” ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ ๋ฐ์Šคํฌํ†ฑ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ์™ธ๋ถ€ ์„œ๋ฒ„, ์˜์ˆ˜์ฆ ํ”„๋ฆฐํ„ฐ, ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๊ฐ€ ํ•œ ๋ฒˆ์— ๋งž๋ฌผ๋ ค ๋™์ž‘ํ•˜๋Š” ํ†ตํ•ฉ ์ง€์ ์ด๋ผ๋Š” ์ ์ด ๊ฐ€์žฅ ์ธ์ƒ ๊นŠ์—ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๋ฌธ์ œ๋Š” ์ œ๊ฐ€ ๊ทธ๋™์•ˆ ์ตํ˜€์˜จ ๋ฌธ์ œ ์˜์‹๊ณผ ๋‹ฟ์•„ ์žˆ์Šต๋‹ˆ๋‹ค. ์…€ํ”„ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์„œ๋น„์Šค CloudSharp์—์„œ๋Š” Local FSยทMinIOยทS3๋ฅผ ๋™์ผํ•œ storage_key๋กœ ์ถ”์ƒํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์šด์˜ ํ™˜๊ฒฝ์ด ๋‹ฌ๋ผ์ ธ๋„ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์—ˆ๊ณ , tusd๋ผ๋Š” ์™ธ๋ถ€ Go ํ”„๋กœ์„ธ์Šค์™€ hook์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์—ฐ๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ `TusBlazorClient`๋ฅผ NuGet์— ๋ฐฐํฌํ•˜๋ฉด์„œ C# ์ฝ”๋“œ์™€ JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ง์ ‘ ์ž‡๋Š” ๊ฒฝ๊ณ„ ์„ค๊ณ„๋ฅผ ๊ฒฝํ—˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ์‹คํ–‰ ํ™˜๊ฒฝ๊ณผ ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ํ•œ ๋ฒˆ์— ๋‹ค๋ค„์•ผ ํ•œ๋‹ค๋Š” ์ ์—์„œ, ๊ท€์‚ฌ์˜ ์ ์ฃผ์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ํ’€๊ณ  ์žˆ๋Š” ๋ฌธ์ œ์— ์ œ๊ฐ€ ๊ฐ€์žฅ ์ž˜ ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. + +--- + +## 2. ์—…๋ฌด ์‹œ ๊ฐ•์  โ€” ์™ธ๋ถ€ ์‹œ์Šคํ…œ์„ ์•ˆ์ •์ ์œผ๋กœ ์ž‡๋Š” ์„ค๊ณ„ + +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ **C# ์ฝ”๋“œ์™€ ์™ธ๋ถ€ ์‹œ์Šคํ…œ ์‚ฌ์ด์˜ ๊ฒฝ๊ณ„๋ฅผ ์•ˆ์ •์ ์œผ๋กœ ์„ค๊ณ„ํ•˜๋Š” ๋Šฅ๋ ฅ**์ž…๋‹ˆ๋‹ค. + +์ด ๊ฐ•์ ์€ Blazor WebAssembly์šฉ NuGet ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ `TusBlazorClient`๋ฅผ ๋‹จ๋…์œผ๋กœ ์„ค๊ณ„ยท๊ตฌํ˜„ยท๋ฐฐํฌํ•œ ๊ฒฝํ—˜์—์„œ ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. Blazor์˜ ์ˆœ์ˆ˜ C# ์ฝ”๋“œ๋Š” ๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ ์—…๋กœ๋“œ์—์„œ ๋ฉ”๋ชจ๋ฆฌยท์†๋„ ํ•œ๊ณ„๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒ€์ฆ๋œ JavaScript ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ `tus-js-client`๋ฅผ C# API๋กœ ๊ฐ์‹ธ๋Š” ๋ž˜ํผ๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +๊ฐ€์žฅ ์–ด๋ ค์› ๋˜ ์ง€์ ์€ **JSโ†”.NET ์ฝœ๋ฐฑ ๋ธŒ๋ฆฟ์ง€ ์„ค๊ณ„**์˜€์Šต๋‹ˆ๋‹ค. tus-js-client๋Š” ์ง„ํ–‰๋ฅ ยท์˜ค๋ฅ˜ยท์™„๋ฃŒ๋ฅผ JS ์ฝœ๋ฐฑ์œผ๋กœ ํ†ต์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด C# ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋Š” JSON ์ง๋ ฌํ™”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด JS์— ๊ทธ๋Œ€๋กœ ๋„˜๊ธธ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ์„ ์ผ๋ถ€๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” JSโ†’.NET ํ˜ธ์ถœ ์ž์ฒด๊ฐ€ ๋ถˆํ•„์š”ํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋˜๋Š” ๋ฌธ์ œ๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. + +์ €๋Š” ๋ชจ๋“  ์ฝœ๋ฐฑ์„ `TusOptionJsInvoke`๋ผ๋Š” ํ•œ ๊ฐ์ฒด์— ๋ชจ์•„ `DotNetObjectReference`๋กœ JS์— ํ•œ ๋ฒˆ๋งŒ ์ „๋‹ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ ๋ณธ๋ฌธ์€ `[JsonIgnore]`๋กœ ์ง๋ ฌํ™”์—์„œ ์ œ์™ธํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— `TusOptionNullCheck` ๋ฉ”ํƒ€์ •๋ณด๋ฅผ ๋ณ„๋„๋กœ ๋‘์–ด, JS ์ธก์—์„œ null ์ฝœ๋ฐฑ์— ๋Œ€ํ•œ `invokeMethodAsync`๋ฅผ ์•„์˜ˆ ๊ฑด๋„ˆ๋›ฐ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ƒํƒœ ๋ณ€๊ฒฝ์ด ์—†๋Š” `OnBeforeRequest`/`OnAfterResponse`๋Š” ๋™๊ธฐ ํ˜ธ์ถœ๋กœ ๋ฐ”๊ฟ” ๋งˆ์ƒฌ๋ง ๋น„์šฉ์„ ์ถ”๊ฐ€๋กœ ์ค„์˜€์Šต๋‹ˆ๋‹ค. + +๊ทธ ๊ฒฐ๊ณผ ์‚ฌ์šฉ์ž๋Š” JavaScript ํ•œ ์ค„ ์—†์ด C#๋งŒ์œผ๋กœ **18๊ฐœ ์˜ต์…˜๊ณผ 7์ข… ์ฝœ๋ฐฑ์„ ๋ชจ๋‘ ์‚ฌ์šฉ**ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์—…๋กœ๋“œ, ์ค‘๋‹จยท์žฌ๊ฐœ, ์˜ต์…˜ ๋™์  ๋ณ€๊ฒฝ, ์žฌ์‹œ๋„ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ **9๊ฐœ์˜ Selenium E2E ํ…Œ์ŠคํŠธ**๋กœ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ NuGet์— 1.0.1 ๋ฒ„์ „์œผ๋กœ ๊ณต๊ฐœ ๋ฐฐํฌํ•ด ์™ธ๋ถ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์šด์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. + +์ด ๊ฒฝํ—˜์€ ํŒจ์Šค์˜ค๋” ์ ์ฃผ์šฉ ํ”„๋กœ๊ทธ๋žจ์ด ํ’€์–ด์•ผ ํ•  ๋ฌธ์ œ์™€ ์ง์ ‘ ๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์˜์ˆ˜์ฆ ํ”„๋ฆฐํ„ฐ, ๋ฐ”์ฝ”๋“œ ์Šค์บ๋„ˆ, REST API, Socket ํ†ต์‹ , ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋Š” ๋ชจ๋‘ C# ์™ธ๋ถ€์— ์žˆ๋Š” ์‹œ์Šคํ…œ์ž…๋‹ˆ๋‹ค. ์ด๋“ค์„ C#์—์„œ ์–ด๋–ป๊ฒŒ ์•ˆ์ „ํ•˜๊ฒŒ ์—ฐ๊ฒฐํ•˜๋А๋ƒ๊ฐ€ ๊ณง ์•ˆ์ •์„ฑ์˜ ํ•ต์‹ฌ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ์™ธ๋ถ€ ์˜์กด์„ฑ๊ณผ C# ์‚ฌ์ด์˜ ๊ฒฝ๊ณ„๋ฅผ ๋ช…ํ™•ํžˆ ์„ค๊ณ„ํ•ด ์ฃผ๋ฌธ ๋ˆ„๋ฝ๊ณผ ์žฅ์• ๋ฅผ ์ค„์ด๋Š” ๋ฐ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ โ€” ๊ตฌํ˜•๋ถ€ํ„ฐ ์ตœ์‹ ๊นŒ์ง€, ๊ฐ™์€ ์•ˆ์ •์„ฑ์œผ๋กœ + +์ž…์‚ฌ ํ›„์—๋Š” **์ˆ˜๋งŒ ๊ฐœ ๋งค์žฅ์˜ ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์—์„œ ๋™์ผํ•œ ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ•˜๋Š” WPF ๊ฐœ๋ฐœ์ž**๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ๋Š” ArgoCDยทArgo Rollouts ๊ธฐ๋ฐ˜ Canary ๋ฐฐํฌ, Istio Service Mesh, Kafka ์ „์šฉ Gateway, 0๋‹จ๊ณ„ ๋ชจ๋“œ๋ฅผ ์šด์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋‘ "์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š” ๊ฒ€์ฆ"์„ ํ•ต์‹ฌ ๊ฐ€์น˜๋กœ ๋‘” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ ์ฃผ์šฉ PC ํ”„๋กœ๊ทธ๋žจ์—์„œ๋„ ๊ฐ™์€ ๊ธฐ์ค€์œผ๋กœ ์•ˆ์ •์„ฑ์„ ๋Œ์–ด์˜ฌ๋ฆฌ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +**์ž…์‚ฌ ์ดˆ๊ธฐ**์—๋Š” ํŒจ์Šค์˜ค๋” ์ ์ฃผ์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ฅผ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ์ง‘์ค‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜์ˆ˜์ฆ ํ”„๋ฆฐํ„ฐยทPOS PCยทํ‚ค์˜ค์Šคํฌ์—์„œ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€, REST/Socket ํ†ต์‹ ๊ณผ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ์—ฐ๋™์ด ์–ด๋–ป๊ฒŒ ๋งž๋ฌผ๋ ค ์žˆ๋Š”์ง€ ์ตํžˆ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +**1๋…„ ์•ˆ**์—๋Š” ์ ์ง„์  ๋ฆฌํŒฉํ† ๋ง ๊ณผ์ •์— ์ง์ ‘ ์ฐธ์—ฌํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. CloudSharp์—์„œ Clean Architecture๋กœ ๊ณ„์ธต ๊ฒฝ๊ณ„๋ฅผ ๋ถ„๋ฆฌํ•ด๋ณธ ๊ฒฝํ—˜์ด ๋„์›€์ด ๋  ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์งยทํ†ต์‹ ยทUI๊ฐ€ ์„ž์—ฌ ์žˆ๋Š” ๋ถ€๋ถ„์„ ๋‹จ๊ณ„์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ๊ตฌํ˜• PC์™€ ์ตœ์‹  ํ‚ค์˜ค์Šคํฌ์˜ ๋™์ž‘ ์ฐจ์ด๋ฅผ ์žก์•„๋‚ด๋Š” ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋ฅผ ๋ณด๊ฐ•ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. + +**3๋…„ ์•ˆ**์—๋Š” ์ฃผ๋ฌธ ์ ‘์ˆ˜ยท๊ฒฐ์ œ ์ƒํƒœยทํ•˜๋“œ์›จ์–ด ์ œ์–ด ํ๋ฆ„์˜ ์‹ ๋ขฐ์„ฑ ํŒจํ„ด์„ ํŒ€ ๋‚ด์—์„œ ์ •๋ฆฌํ•ด ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋Œ€๊ทœ๋ชจ ์šด์˜ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ CloudSharp์—์„œ Finalize ๋™์‹œ์„ฑ์„ CAS๋กœ ๋‹ค๋ฃจ๊ณ  Recovery Worker๋กœ ๊ต์ฐฉ์„ ์ž๋™ ํ•ด์†Œํ•œ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์žฅ์• ๋ฅผ ๋ถ„์„ํ•ด ๊ตฌ์กฐ์— ๋ฐ˜์˜ํ•˜๋Š” ์—ญ๋Ÿ‰์„ ์ฑ„์›Œ๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค. + +--- + +## 4. ์„ฑ์žฅ ๊ณผ์ • โ€” ์™ธ๋ถ€์— ๋‹ฟ๋Š” ์ฝ”๋“œ์˜ ๋ฌด๊ฒŒ + +์ €๋Š” **"๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ"์—์„œ "์™ธ๋ถ€์— ๋…ธ์ถœ๋˜์–ด๋„ ์•ˆ์ •์ ์ธ ์ฝ”๋“œ"**๋กœ ๊ด€์ ์„ ์˜ฎ๊ฒจ์˜จ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค. + +์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ–ˆ์„ ๋•Œ๋Š” ๊ธฐ๋Šฅ์ด ์ž˜ ๋Œ์•„๊ฐ€๋ฉด ๊ทธ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ด€์ ์ด ๋ฐ”๋€ ๊ณ„๊ธฐ๋Š” `TusBlazorClient`๋ฅผ NuGet์— ๊ณต๊ฐœํ•˜๋ฉด์„œ์˜€์Šต๋‹ˆ๋‹ค. ๋‚ด ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๊ฐ€ ์˜์กดํ•˜๊ธฐ ์‹œ์ž‘ํ•˜์ž, ์ž‘์€ ๊ฒฐ์ • ํ•˜๋‚˜๊ฐ€ ์™ธ๋ถ€ ์‚ฌ์šฉ์ž์˜ ์ฝ”๋“œ๋ฅผ ๊นจ๋œจ๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฝœ๋ฐฑ์„ `[JsonIgnore]`๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , `IAsyncDisposable`๋กœ JS ๋ชจ๋“ˆ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ •๋ฆฌํ•œ ๊ฒƒ์€ ๋ชจ๋‘ ๊ฐ™์€ ์งˆ๋ฌธ์—์„œ ๋‚˜์™”์Šต๋‹ˆ๋‹ค. "์‚ฌ์šฉ์ž์˜ ์ฝ”๋“œ๊ฐ€ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋•Œ๋ฌธ์— ์–ด๋–ป๊ฒŒ ๊นจ์งˆ ์ˆ˜ ์žˆ๋Š”๊ฐ€." + +์ด ๊ฒฝํ—˜ ์ดํ›„ CloudSharp์—์„œ๋„ ๊ฐ™์€ ํƒœ๋„๋กœ ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. JWT ๋Œ€์‹  Opaque Session Token์„ ์„ ํƒํ•ด ๊ถŒํ•œ ๋ณ€๊ฒฝ์ด ์ฆ‰์‹œ ๋ฐ˜์˜๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. Finalize ์ค‘๋ณต์€ CAS์™€ Recovery Worker๋กœ ๋ง‰์•˜๊ณ , ์™ธ๋ถ€ ๊ณต๊ฐœ API์—๋Š” Zero Information Leak ์ •์ฑ…์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋‘ "์™ธ๋ถ€์—์„œ ์ด ์ฝ”๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž˜๋ชป ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋Š”๊ฐ€"๋ฅผ ๋จผ์ € ๋ฌป๋Š” ์‚ฌ๊ณ ์—์„œ ๋‚˜์˜จ ์„ ํƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. + +๊ท€์‚ฌ์˜ ์ ์ฃผ์šฉ ํ”„๋กœ๊ทธ๋žจ์€ ์ „๊ตญ ์ˆ˜๋งŒ ๊ฐœ ๋งค์žฅ์—์„œ ๋งค์ผ ์˜์—…์˜ ๊ฐ€์žฅ ์•ž๋‹จ์— ์„œ ์žˆ๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์™ธ๋ถ€์— ๋‹ฟ๋Š” ์ฝ”๋“œ์˜ ๋ฌด๊ฒŒ๋ฅผ ์ตํ˜€์˜จ ๋งŒํผ, ์ ์ฃผ๋‹˜๊ณผ ์†๋‹˜ ์–‘์ชฝ์ด ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“œ๋Š” ๋ฐ ์ฑ…์ž„๊ฐ ์žˆ๊ฒŒ ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. \ No newline at end of file diff --git a/ํ”„๋กฌํ”„ํŠธ/it_developer_self_intro_convention_context.md b/ํ”„๋กฌํ”„ํŠธ/it_developer_self_intro_convention_context.md new file mode 100644 index 0000000..2b0da11 --- /dev/null +++ b/ํ”„๋กฌํ”„ํŠธ/it_developer_self_intro_convention_context.md @@ -0,0 +1,684 @@ +# IT ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ ์ปจ๋ฒค์…˜ ์ปจํ…์ŠคํŠธ + +## 0. ๋ฌธ์„œ ๋ชฉ์  + +์ด ๋ฌธ์„œ๋Š” IT ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ๋•Œ ํ•ญ์ƒ ์ฐธ์กฐํ•ด์•ผ ํ•˜๋Š” **์ž‘์„ฑ ์ปจ๋ฒค์…˜ ๋ฌธ์„œ**๋‹ค. + +์ž๊ธฐ์†Œ๊ฐœ์„œ๋Š” ๋‹จ์ˆœํ•œ ์„ฑ๊ฒฉ ์†Œ๊ฐœ๋‚˜ ๋‹ค์ง๋ฌธ์ด ์•„๋‹ˆ๋ผ, ์•„๋ž˜ ์„ธ ๊ฐ€์ง€๋ฅผ ์„ค๋“ํ•˜๋Š” ๋ฌธ์„œ๋กœ ์ž‘์„ฑํ•œ๋‹ค. + +1. **์™œ ์ด ํšŒ์‚ฌ์ธ์ง€** +2. **์™œ ๋‚ด๊ฐ€ ์ด ์ง๋ฌด์— ์ ํ•ฉํ•œ์ง€** +3. **์ž…์‚ฌ ํ›„ ์–ด๋–ป๊ฒŒ ์„ฑ์žฅํ•˜๊ณ  ํšŒ์‚ฌ์— ๊ธฐ์—ฌํ•  ๊ฒƒ์ธ์ง€** + +์ž‘์„ฑ ๋Œ€์ƒ์€ ์‹ ์ž… ๊ฐœ๋ฐœ์ž ๋ฐ ์ฃผ๋‹ˆ์–ด ๊ฐœ๋ฐœ์ž ๊ธฐ์ค€์ด๋ฉฐ, ๋ฐฑ์—”๋“œ / ํ”„๋ก ํŠธ์—”๋“œ / ํ’€์Šคํƒ / ๋ฐ์ดํ„ฐ ์—”์ง€๋‹ˆ์–ด / DevOps / ์ธํ”„๋ผ / QA / ๋ชจ๋ฐ”์ผ ์ง๋ฌด์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. + +--- + +## 1. ์ตœ์ƒ์œ„ ์ž‘์„ฑ ์›์น™ + +### 1-1. ๋ชจ๋“  ๋ฌธํ•ญ์€ ํšŒ์‚ฌ์™€ ์—ฐ๊ฒฐํ•œ๋‹ค + +์ž๊ธฐ์†Œ๊ฐœ์„œ์˜ ๋ชจ๋“  ๋ฌธํ•ญ์—๋Š” ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ ๋‘ ์š”์†Œ๊ฐ€ ํ•จ๊ป˜ ๋“ค์–ด๊ฐ€์•ผ ํ•œ๋‹ค. + +| ์š”์†Œ | ์„ค๋ช… | +|---|---| +| ํšŒ์‚ฌ ์ •๋ณด | ์ฑ„์šฉ๊ณต๊ณ , ํ™ˆํŽ˜์ด์ง€, ์„œ๋น„์Šค, ๊ธฐ์ˆ  ๋ฐฉํ–ฅ, ์‚ฌ์—… ๋ฐฉํ–ฅ, ๊ฐœ๋ฐœ ๋ฌธํ™” | +| ๋‚ด ๊ฒฝํ—˜ | ํ”„๋กœ์ ํŠธ, ํ˜‘์—…, ์„ฑ๋Šฅ ๊ฐœ์„ , ์šด์˜ ๊ฒฝํ—˜, ํ•™์Šต ๊ฒฝํ—˜, ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ฒฝํ—˜ | + +๊ธฐ๋ณธ ๊ณต์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. + +```text +ํšŒ์‚ฌ ๋ถ„์„ + ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ = ์„ค๋“๋ ฅ ์žˆ๋Š” ์ž๊ธฐ์†Œ๊ฐœ์„œ +``` + +ํšŒ์‚ฌ ์ด๋ฆ„์„ ๋‹ค๋ฅธ ํšŒ์‚ฌ๋ช…์œผ๋กœ ๋ฐ”๊ฟ”๋„ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฌธ์žฅ์€ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. + +--- + +### 1-2. ๋ฌธํ•ญ๋งˆ๋‹ค ์—ญํ• ์„ ๋ถ„๋ฆฌํ•œ๋‹ค + +๊ฐ ๋ฌธํ•ญ์€ ์„œ๋กœ ๋‹ค๋ฅธ ์งˆ๋ฌธ์— ๋‹ตํ•ด์•ผ ํ•œ๋‹ค. + +| ๋ฌธํ•ญ | ํ•ต์‹ฌ ์งˆ๋ฌธ | ์ค‘์‹ฌ ๋‚ด์šฉ | +|---|---|---| +| ์ง€์›๋™๊ธฐ | ์™œ ์ด ํšŒ์‚ฌ์— ์ง€์›ํ–ˆ๋Š”๊ฐ€ | ํšŒ์‚ฌ ๊ฐ•์  + ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ | +| ์—…๋ฌด ์‹œ ๊ฐ•์  | ์–ด๋–ค ์—ญ๋Ÿ‰์œผ๋กœ ๋ฐ”๋กœ ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€ | ๊ฐ•์  1๊ฐœ + ์‚ฌ๋ก€ 1๊ฐœ + ์ˆ˜์น˜ ๊ฒฐ๊ณผ | +| ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ | ์–ด๋–ป๊ฒŒ ์„ฑ์žฅํ•ด ์žฅ๊ธฐ์ ์œผ๋กœ ๊ธฐ์—ฌํ•  ๊ฒƒ์ธ๊ฐ€ | ํšŒ์‚ฌ ๋ฐฉํ–ฅ + ์„ฑ์žฅ ๊ณ„ํš + ๊ธฐ์—ฌ ๋ฐฉ์‹ | +| ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • | ์–ด๋–ค ๊ฐœ๋ฐœ์ž์ธ๊ฐ€ | ๊ฐœ๋ฐœ์ž ์ •์ฒด์„ฑ์ด ํ˜•์„ฑ๋œ ๊ณ„๊ธฐ | + +๊ฐ™์€ ์‚ฌ๋ก€๋ฅผ ์—ฌ๋Ÿฌ ๋ฌธํ•ญ์—์„œ ๊ทธ๋Œ€๋กœ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ™์€ ํ”„๋กœ์ ํŠธ๋ฅผ ์“ฐ๋”๋ผ๋„ ์ดˆ์ ์€ ๋‹ค๋ฅด๊ฒŒ ๋‘”๋‹ค. + +--- + +### 1-3. ๋‘๊ด„์‹์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค + +๊ฐ ๋ฌธํ•ญ์˜ ์ฒซ ๋ฌธ์žฅ์—์„œ ๊ฒฐ๋ก ์ด ๋ณด์—ฌ์•ผ ํ•œ๋‹ค. + +์ข‹์€ ์ฒซ ๋ฌธ์žฅ์€ ์•„๋ž˜ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•œ๋‹ค. + +- ๋ฌธํ•ญ์˜ ๋‹ต์ด ๋ฐ”๋กœ ๋ณด์ธ๋‹ค. +- ํšŒ์‚ฌ ๋˜๋Š” ์ง๋ฌด์™€ ์—ฐ๊ฒฐ๋œ๋‹ค. +- ์ถ”์ƒ์ ์ธ ๊ฐ์ • ํ‘œํ˜„์ด ์•„๋‹ˆ๋ผ ํŒ๋‹จ์ด ๋“œ๋Ÿฌ๋‚œ๋‹ค. + +์˜ˆ์‹œ: + +```text +์ €๋Š” ๊ท€์‚ฌ์˜ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ๊ณ ๋„ํ™” ๋ฐฉํ–ฅ์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ์„ฑ๋Šฅ ๋ฌธ์ œ์˜ ์›์ธ์„ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๊ณ  ๊ฐœ์„ ํ•˜๋Š” ์—ญ๋Ÿ‰์ž…๋‹ˆ๋‹ค. +์ž…์‚ฌ ํ›„์—๋Š” ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ•จ๊ป˜ ๊ณ ๋ฏผํ•˜๋Š” ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. +``` + +--- + +### 1-4. ์ถ”์ƒ์–ด๋Š” ์‚ฌ๋ก€๋กœ ์ฆ๋ช…ํ•œ๋‹ค + +์•„๋ž˜ ํ‘œํ˜„์€ ๋‹จ๋…์œผ๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. + +| ์•ฝํ•œ ํ‘œํ˜„ | ๋ฌธ์ œ์  | ๋Œ€์ฒด ๋ฐฉํ–ฅ | +|---|---|---| +| ์ฑ…์ž„๊ฐ์ด ์žˆ์Šต๋‹ˆ๋‹ค | ์ฆ๋ช… ์—†๋Š” ์ž๊ธฐ ํ‰๊ฐ€ | ๋๊นŒ์ง€ ๋งก์•„ ํ•ด๊ฒฐํ•œ ์‚ฌ๋ก€๋กœ ์ฆ๋ช… | +| ํ˜‘์—…์„ ์ž˜ํ•ฉ๋‹ˆ๋‹ค | ๋ฐฉ์‹์ด ๋ณด์ด์ง€ ์•Š์Œ | ์—ญํ•  ๋ถ„๋‹ด, ์ผ์ • ์กฐ์œจ, ๋ฆฌ๋ทฐ ๋ฐฉ์‹ ์„ค๋ช… | +| ๋ฌธ์ œ ํ•ด๊ฒฐ ๋Šฅ๋ ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค | ์–ด๋–ค ๋ฌธ์ œ์ธ์ง€ ๋ถˆ๋ช…ํ™• | ๋ฌธ์ œ ์›์ธ, ํŒ๋‹จ, ํ•ด๊ฒฐ ๊ณผ์ • ์ œ์‹œ | +| ์„ฑ์‹คํ•ฉ๋‹ˆ๋‹ค | ๊ฐœ๋ฐœ ์—ญ๋Ÿ‰์œผ๋กœ ๋ณด์ด์ง€ ์•Š์Œ | ๊พธ์ค€ํžˆ ํ•™์Šตํ•ด ์ ์šฉํ•œ ๊ฒฐ๊ณผ ์ œ์‹œ | + +๊ธฐ๋ณธ ๊ณต์‹: + +```text +์ถ”์ƒ์  ๊ฐ•์  ์„ ์–ธ ๊ธˆ์ง€ +๊ฐ•์  1๊ฐœ โ†’ ์‚ฌ๋ก€ 1๊ฐœ โ†’ ๋‚ด๊ฐ€ ํ•œ ํ–‰๋™ โ†’ ๊ฒฐ๊ณผ ์ˆ˜์น˜ โ†’ ์ž…์‚ฌ ํ›„ ์—ฐ๊ฒฐ +``` + +--- + +## 2. ์ž…๋ ฅ ์ •๋ณด ์ •๋ฆฌ ์ปจ๋ฒค์…˜ + +์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ ์ „ ๋ฐ˜๋“œ์‹œ ์•„๋ž˜ ์ •๋ณด๋ฅผ ๋จผ์ € ์ •๋ฆฌํ•œ๋‹ค. + +```markdown +# ๊ธฐ์—… / ์ง๋ฌด ๋ถ„์„ ๋ฉ”๋ชจ + +## ๊ธฐ๋ณธ ์ •๋ณด +- ํšŒ์‚ฌ๋ช…: +- ์ง€์› ์ง๋ฌด: +- ์ง€์› ํŒ€ / ์„œ๋น„์Šค: +- ์„œ๋น„์Šค ๋Œ€์ƒ ์‚ฌ์šฉ์ž: + +## ์ฑ„์šฉ๊ณต๊ณ  ๋ถ„์„ +- ํ•„์ˆ˜ ๊ธฐ์ˆ  ์Šคํƒ: +- ์šฐ๋Œ€ ๊ธฐ์ˆ  ์Šคํƒ: +- ๋‹ด๋‹น ์—…๋ฌด: +- ์š”๊ตฌ ์—ญ๋Ÿ‰: +- ๋ฐ˜๋ณต ๋“ฑ์žฅ ํ‚ค์›Œ๋“œ: + +## ํšŒ์‚ฌ / ์„œ๋น„์Šค ๋ถ„์„ +- ํ•ต์‹ฌ ์„œ๋น„์Šค: +- ํ•ด๊ฒฐํ•˜๋Š” ๋ฌธ์ œ: +- ์ฃผ์š” ๊ณ ๊ฐ: +- ์ตœ๊ทผ ์‚ฌ์—… ๋ฐฉํ–ฅ: +- ์ธ์ƒ ๊นŠ์€ ์ง€์ : + +## ๊ธฐ์ˆ  / ๊ฐœ๋ฐœ ๋ฌธํ™” ๋ถ„์„ +- ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ˆ : +- ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ์—์„œ ํ™•์ธํ•œ ๋ฌธ์ œ: +- ์•„ํ‚คํ…์ฒ˜ / ์ธํ”„๋ผ ๋ฐฉํ–ฅ: +- ์„ฑ๋Šฅ / ์šด์˜ / ๋ณด์•ˆ ๊ด€๋ จ ์ด์Šˆ: +- ๊ฐ•์กฐํ•˜๋Š” ๊ฐ€์น˜: +- ์ผํ•˜๋Š” ๋ฐฉ์‹: +- ๊ฐœ๋ฐœ ๋ฌธํ™”: + +## ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ ํ›„๋ณด +- ๊ฒฝํ—˜ 1: + - ํ”„๋กœ์ ํŠธ๋ช…: + - ์‚ฌ์šฉ ๊ธฐ์ˆ : + - ๋ฌธ์ œ ์ƒํ™ฉ: + - ๋‚ด๊ฐ€ ํ•œ ํ–‰๋™: + - ๊ฒฐ๊ณผ: + - ์—ฐ๊ฒฐ ๊ฐ€๋Šฅํ•œ ํšŒ์‚ฌ ์š”๊ตฌ์‚ฌํ•ญ: +- ๊ฒฝํ—˜ 2: +- ๊ฒฝํ—˜ 3: +``` + +--- + +## 3. ๋ฌธํ•ญ๋ณ„ ์ž‘์„ฑ ์ปจ๋ฒค์…˜ + +## 3-1. ์ง€์›๋™๊ธฐ + +### ํ•ต์‹ฌ ์งˆ๋ฌธ + +```text +์™œ ์ด ํšŒ์‚ฌ์— ์ง€์›ํ–ˆ๋Š”๊ฐ€? +``` + +### ์ž‘์„ฑ ๋ชฉํ‘œ + +์ง€์›๋™๊ธฐ๋Š” ํšŒ์‚ฌ ์นญ์ฐฌ์ด ์•„๋‹ˆ๋‹ค. + +์•„๋ž˜ ๋‘ ๊ฐ€์ง€๊ฐ€ ๋™์‹œ์— ๋“œ๋Ÿฌ๋‚˜์•ผ ํ•œ๋‹ค. + +1. **Why this company**: ์™œ ์ด ํšŒ์‚ฌ์ธ๊ฐ€ +2. **Why me**: ์™œ ๋‚ด๊ฐ€ ์ด ํšŒ์‚ฌ์™€ ๋งž๋Š”๊ฐ€ + +### ๊ถŒ์žฅ ๊ตฌ์กฐ + +```text +โ‘  ํšŒ์‚ฌ๋ฅผ ์•Œ๊ฒŒ ๋œ ๊ณ„๊ธฐ โ†’ 1~2๋ฌธ์žฅ +โ‘ก ํšŒ์‚ฌ์˜ ๊ธฐ์ˆ  / ์‚ฌ์—… ๊ฐ•์  ๋ฐœ๊ฒฌ โ†’ 2~3๋ฌธ์žฅ +โ‘ข ๊ทธ๊ฒƒ์ด ์ธ์ƒ ๊นŠ์—ˆ๋˜ ์ด์œ  โ†’ 1~2๋ฌธ์žฅ +โ‘ฃ ๋‚ด ๊ฒฝํ—˜๊ณผ์˜ ์—ฐ๊ฒฐ โ†’ 2~3๋ฌธ์žฅ +โ‘ค ์ง€์› ๊ฒฐ๋ก  โ†’ 1๋ฌธ์žฅ +``` + +### ์ž‘์„ฑ ๊ณต์‹ + +```text +[ํšŒ์‚ฌ ๊ฐ•์ ] + [๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ] = ์ง€์› ์ด์œ  +``` + +### ์ฒซ ๋ฌธ์žฅ ํ…œํ”Œ๋ฆฟ + +```text +์ €๋Š” ๊ท€์‚ฌ์˜ ______์— ์ฃผ๋ชฉํ•ด ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. +์ €๋Š” ๊ท€์‚ฌ๊ฐ€ ______์„ ํ†ตํ•ด ______ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ–ˆ์Šต๋‹ˆ๋‹ค. +์ €๋Š” ๊ท€์‚ฌ์˜ ______ ์„œ๋น„์Šค ๋ฐฉํ–ฅ์ด ์ œ๊ฐ€ ๊ฒฝํ—˜ํ•œ ______ ๋ฌธ์ œ์˜์‹๊ณผ ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. +``` + +### ๋ฐ˜๋“œ์‹œ ํฌํ•จํ•  ๋‚ด์šฉ + +- ์ฑ„์šฉ๊ณต๊ณ  / ํ™ˆํŽ˜์ด์ง€ / ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ / ์„œ๋น„์Šค ํŽ˜์ด์ง€์—์„œ ํ™•์ธํ•œ ๊ตฌ์ฒด ์ •๋ณด +- ํšŒ์‚ฌ์˜ ๊ธฐ์ˆ  ๋˜๋Š” ์‚ฌ์—… ๋ฐฉํ–ฅ์— ๋Œ€ํ•œ ๋‚ด ํŒ๋‹จ +- ๋‚ด ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜๊ณผ์˜ ์—ฐ๊ฒฐ +- ๋งˆ์ง€๋ง‰ ์ง€์› ๊ฒฐ๋ก  + +### ํ”ผํ•ด์•ผ ํ•  ๋ฌธ์žฅ + +```text +๊ท€์‚ฌ๋Š” ์—…๊ณ„๋ฅผ ์„ ๋„ํ•˜๋Š” ๊ธฐ์—…์œผ๋กœ... +4์ฐจ ์‚ฐ์—…ํ˜๋ช… ์‹œ๋Œ€์—... +์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„ ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค. +ํ˜์‹ ์ ์ธ ํšŒ์‚ฌ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. +``` + +### ์ข‹์€ ๋งˆ๋ฌด๋ฆฌ ์˜ˆ์‹œ + +```text +์ด์ฒ˜๋Ÿผ ๊ท€์‚ฌ์˜ ๊ธฐ์ˆ  ๋ฐฉํ–ฅ๊ณผ ์ œ๊ฐ€ ํ”„๋กœ์ ํŠธ์—์„œ ์Œ“์•„์˜จ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ฒฝํ—˜์ด ๋งž๋‹ฟ์•„ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ณ , ์ด ์ ์ด ์ง€์›์„ ๊ฒฐ์‹ฌํ•œ ๊ฐ€์žฅ ํฐ ์ด์œ ์ž…๋‹ˆ๋‹ค. +``` + +--- + +## 3-2. ์—…๋ฌด ์‹œ ๊ฐ•์  + +### ํ•ต์‹ฌ ์งˆ๋ฌธ + +```text +์–ด๋–ค ์—ญ๋Ÿ‰์œผ๋กœ ๋ฐ”๋กœ ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? +``` + +### ์ž‘์„ฑ ๋ชฉํ‘œ + +์—…๋ฌด ๊ฐ•์  ๋ฌธํ•ญ์€ ์ฑ„์šฉ๊ณต๊ณ  ์š”๊ตฌ ์—ญ๋Ÿ‰๊ณผ ๊ฐ€์žฅ ์ง์ ‘์ ์œผ๋กœ ์—ฐ๊ฒฐํ•ด์•ผ ํ•œ๋‹ค. + +๊ฐ•์ ์€ ๋งŽ์ด ๋‚˜์—ดํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ€์žฅ ์„ค๋“๋ ฅ ์žˆ๋Š” ๊ฐ•์  **1๊ฐœ**๋ฅผ ๊ณ ๋ฅด๊ณ , ๋Œ€ํ‘œ ์‚ฌ๋ก€ **1๊ฐœ**๋กœ ์ฆ๋ช…ํ•œ๋‹ค. + +### ๊ถŒ์žฅ ๊ตฌ์กฐ: STAR + +| ๋‹จ๊ณ„ | ๋‚ด์šฉ | ๋ถ„๋Ÿ‰ ๋น„์ค‘ | +|---|---|---:| +| S: Situation | ์–ด๋–ค ํ”„๋กœ์ ํŠธ / ์–ด๋–ค ๋ฌธ์ œ์˜€๋Š”๊ฐ€ | 15% | +| T: Task | ๋‚ด๊ฐ€ ๋งก์€ ์—ญํ• ์€ ๋ฌด์—‡์ธ๊ฐ€ | 10% | +| A: Action | ๋‚ด๊ฐ€ ํ•œ ๊ธฐ์ˆ ์  ํŒ๋‹จ๊ณผ ์‹คํ–‰์€ ๋ฌด์—‡์ธ๊ฐ€ | 45% | +| R: Result | ์–ด๋–ค ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค์—ˆ๋Š”๊ฐ€ | 20% | +| ์—ฐ๊ฒฐ | ์ž…์‚ฌ ํ›„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ๊ฒƒ์ธ๊ฐ€ | 10% | + +Action์ด ๊ฐ€์žฅ ๊ธธ์–ด์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ธฐ์ˆ ์  ํŒ๋‹จ๊ณผ ๋ฌธ์ œ ํ•ด๊ฒฐ ๋ฐฉ์‹์ด ๋“œ๋Ÿฌ๋‚˜์•ผ ํ•œ๋‹ค. + +### ์ž‘์„ฑ ๊ณต์‹ + +```text +[๊ฐ•์  1๊ฐœ ์„ ์–ธ] + [์‚ฌ๋ก€ 1๊ฐœ ์ฆ๋ช…] + [์ˆ˜์น˜ ๊ฒฐ๊ณผ] = ์„ค๋“ +``` + +### ์ฒซ ๋ฌธ์žฅ ํ…œํ”Œ๋ฆฟ + +```text +์ €์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์€ ______์ž…๋‹ˆ๋‹ค. +์ €๋Š” ______ ๋ฌธ์ œ๋ฅผ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๊ณ  ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ๊ฐ•์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. +์ €๋Š” ______์„ ์‹ค์ œ ๊ฒฐ๊ณผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฐœ๋ฐœ ์—ญ๋Ÿ‰์„ ๊ฐ–์ถ”๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. +``` + +### ์ง๋ฌด๋ณ„ ๊ฐ•์  ํ‚ค์›Œ๋“œ + +| ์ง๋ฌด | ๊ฐ•์  ํ›„๋ณด | +|---|---| +| ๋ฐฑ์—”๋“œ | API ์„ค๊ณ„, ์„ฑ๋Šฅ ๊ฐœ์„ , DB ์ตœ์ ํ™”, ์ธ์ฆ/์ธ๊ฐ€, ์žฅ์•  ๋Œ€์‘, ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ | +| ํ”„๋ก ํŠธ์—”๋“œ | ์ปดํฌ๋„ŒํŠธ ์„ค๊ณ„, ๋ Œ๋”๋ง ์ตœ์ ํ™”, ์ ‘๊ทผ์„ฑ, ์ƒํƒœ ๊ด€๋ฆฌ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„  | +| ํ’€์Šคํƒ | ๊ธฐ๋Šฅ ํ๋ฆ„ ์„ค๊ณ„, API-UI ์—ฐ๊ฒฐ, ๋น ๋ฅธ MVP ๊ตฌํ˜„, ๋ฌธ์ œ ๋ฒ”์œ„ ํ†ตํ•ฉ ์ดํ•ด | +| ๋ฐ์ดํ„ฐ | ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„, ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ ๊ด€๋ฆฌ, ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ, ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ | +| DevOps / ์ธํ”„๋ผ | CI/CD, ์ปจํ…Œ์ด๋„ˆ ์šด์˜, ๋ชจ๋‹ˆํ„ฐ๋ง, ์ธํ”„๋ผ ์ž๋™ํ™”, ๋ฐฐํฌ ์•ˆ์ •ํ™” | +| QA | ํ…Œ์ŠคํŠธ ์ž๋™ํ™”, ๊ฒฐํ•จ ๋ถ„์„, ์ปค๋ฒ„๋ฆฌ์ง€ ํ™•๋Œ€, ํ’ˆ์งˆ ํ”„๋กœ์„ธ์Šค ๊ฐœ์„  | +| ๋ชจ๋ฐ”์ผ | ์•ฑ ์„ฑ๋Šฅ ์ตœ์ ํ™”, ์˜คํ”„๋ผ์ธ ๋Œ€์‘, ๋ฐฐํฌ ์ž๋™ํ™”, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„  | + +### ์ˆ˜์น˜ ํ‘œํ˜„ ๊ทœ์น™ + +๊ฐ€๋Šฅํ•˜๋ฉด ๊ฒฐ๊ณผ์—๋Š” ์ˆซ์ž๋ฅผ ๋„ฃ๋Š”๋‹ค. + +| ์˜์—ญ | ์˜ˆ์‹œ | +|---|---| +| ์„ฑ๋Šฅ | ์‘๋‹ต์†๋„ 1.2์ดˆ โ†’ 0.3์ดˆ | +| ๋นŒ๋“œ | ๋นŒ๋“œ ์‹œ๊ฐ„ 40% ๋‹จ์ถ• | +| ํ…Œ์ŠคํŠธ | ์ปค๋ฒ„๋ฆฌ์ง€ 20% โ†’ 65% | +| ์žฅ์•  | ์žฌํ˜„ ์‹œ๊ฐ„ 2์‹œ๊ฐ„ โ†’ 20๋ถ„ | +| DB | ์กฐํšŒ ์„ฑ๋Šฅ 60% ๊ฐœ์„  | +| ๋ฐฐํฌ | ๋ฐฐํฌ ์ฃผ๊ธฐ ์ฃผ 1ํšŒ โ†’ ์ผ 2ํšŒ | +| ๋น„์šฉ | ๋กœ๊ทธ ์ €์žฅ ๋น„์šฉ ์›” 30% ์ ˆ๊ฐ | + +์ •ํ™•ํ•œ ์ˆ˜์น˜๊ฐ€ ์—†์œผ๋ฉด ๋น„์œจ, ๋ฐฐ์ˆ˜, ๋ฒ”์œ„ ํ‘œํ˜„์„ ์‚ฌ์šฉํ•œ๋‹ค. + +```text +๊ธฐ์กด ๋Œ€๋น„ ์ ˆ๋ฐ˜ ์ˆ˜์ค€์œผ๋กœ ์ค„์˜€์Šต๋‹ˆ๋‹ค. +๋„์ž… ์ „๋ณด๋‹ค ์•ฝ 3๋ฐฐ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. +๋ฐ˜๋ณต ์ž‘์—… ์‹œ๊ฐ„์„ 30๋ถ„์—์„œ 5๋ถ„ ๋‚ด์™ธ๋กœ ์ค„์˜€์Šต๋‹ˆ๋‹ค. +``` + +### ํ”ผํ•ด์•ผ ํ•  ๋ฌธ์žฅ + +```text +์ €๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ ๋Šฅ๋ ฅ์ด ๋›ฐ์–ด๋‚ฉ๋‹ˆ๋‹ค. +ํ”„๋กœ์ ํŠธ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋งˆ์ณค์Šต๋‹ˆ๋‹ค. +ํŒ€์›๋“ค๊ณผ ํ˜‘๋ ฅํ•˜์—ฌ ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ๋ƒˆ์Šต๋‹ˆ๋‹ค. +์—ด์‹ฌํžˆ ์ฐธ์—ฌํ–ˆ์Šต๋‹ˆ๋‹ค. +``` + +### ์ข‹์€ ๋งˆ๋ฌด๋ฆฌ ์˜ˆ์‹œ + +```text +์ด ๊ฒฝํ—˜์„ ํ†ตํ•ด ๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋„˜์–ด, ์›์ธ์„ ๋ถ„์„ํ•˜๊ณ  ์ธก์ • ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ๊ฐœ์„ ํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฐฉ์‹์„ ์ตํ˜”์Šต๋‹ˆ๋‹ค. ์ž…์‚ฌ ํ›„์—๋„ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์„œ๋น„์Šค ์•ˆ์ •์„ฑ๊ณผ ํ’ˆ์งˆ ๊ฐœ์„ ์— ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. +``` + +--- + +## 3-3. ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ + +### ํ•ต์‹ฌ ์งˆ๋ฌธ + +```text +์–ด๋–ป๊ฒŒ ์„ฑ์žฅํ•ด ์žฅ๊ธฐ์ ์œผ๋กœ ๊ธฐ์—ฌํ•  ๊ฒƒ์ธ๊ฐ€? +``` + +### ์ž‘์„ฑ ๋ชฉํ‘œ + +์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋‹ค์ง๋ฌธ์ด ์•„๋‹ˆ๋‹ค. + +์•„๋ž˜ ์„ธ ๊ฐ€์ง€๊ฐ€ ๋ณด์—ฌ์•ผ ํ•œ๋‹ค. + +1. ํšŒ์‚ฌ์˜ ๋ฐฉํ–ฅ ์ดํ•ด +2. ๊ทธ ์•ˆ์—์„œ์˜ ๋‚ด ์„ฑ์žฅ ๋ฐฉํ–ฅ +3. ์„ฑ์žฅ ๊ฒฐ๊ณผ๊ฐ€ ํšŒ์‚ฌ ๊ธฐ์—ฌ๋กœ ์ด์–ด์ง€๋Š” ๋ฐฉ์‹ + +### ๊ถŒ์žฅ ๊ตฌ์กฐ + +```text +โ‘  ํšŒ์‚ฌ์˜ ๋ฐฉํ–ฅ ํ™•์ธ โ†’ 1~2๋ฌธ์žฅ +โ‘ก ๊ทธ ์•ˆ์—์„œ ๋‚ด ์„ฑ์žฅ ๋ฐฉํ–ฅ โ†’ 1~2๋ฌธ์žฅ +โ‘ข ์ž…์‚ฌ ์ดˆ๊ธฐ ๋ชฉํ‘œ โ†’ 1~2๋ฌธ์žฅ +โ‘ฃ 1๋…„ ๋ชฉํ‘œ โ†’ 1~2๋ฌธ์žฅ +โ‘ค 3๋…„ ๋ชฉํ‘œ โ†’ 1~2๋ฌธ์žฅ +โ‘ฅ ๋ณด์™„์  + ๋ณด์™„ ๋ฐฉ๋ฒ• โ†’ 1~2๋ฌธ์žฅ +``` + +### ์ž‘์„ฑ ๊ณต์‹ + +```text +[ํšŒ์‚ฌ ๋ฐฉํ–ฅ] + [๋‚˜์˜ ์„ฑ์žฅ ๊ณ„ํš] + [ํšŒ์‚ฌ ๊ธฐ์—ฌ ์—ฐ๊ฒฐ] = ํฌ๋ถ€ +``` + +### ์ฒซ ๋ฌธ์žฅ ํ…œํ”Œ๋ฆฟ + +```text +์ž…์‚ฌ ํ›„์—๋Š” ______ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. +์ž…์‚ฌ ํ›„์—๋Š” ๊ท€์‚ฌ์˜ ______ ๋ฐฉํ–ฅ ์•ˆ์—์„œ ______ ์—ญ๋Ÿ‰์„ ๊ฐ–์ถ˜ ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. +``` + +### ํƒ€์ž„๋ผ์ธ ๊ธฐ์ค€ + +| ์‹œ์  | ํ‚ค์›Œ๋“œ | ์ž‘์„ฑ ๋‚ด์šฉ | +|---|---|---| +| ์ž…์‚ฌ ์ดˆ๊ธฐ | ์ ์‘ | ๋„๋ฉ”์ธ ํŒŒ์•…, ์ฝ”๋“œ๋ฒ ์ด์Šค ์ดํ•ด, ๊ธฐ๋ณธ ์—…๋ฌด ์ˆ˜ํ–‰ | +| 1๋…„ ๋‚ด์™ธ | ๋…๋ฆฝ | ๊ธฐ๋Šฅ ๋‹จ๋… ์ˆ˜ํ–‰, ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์ฐธ์—ฌ, ํ’ˆ์งˆ ๊ฐœ์„  | +| 3๋…„ ๋‚ด์™ธ | ์ฃผ๋„ | ๊ตฌ์กฐ ๊ฐœ์„  ์ œ์•ˆ, ๊ธฐ์ˆ  ๊ณต์œ , ํŒ€ ๊ธฐ์—ฌ ํ™•๋Œ€ | + +### ๋ณด์™„์  ์ž‘์„ฑ ๊ณต์‹ + +๋ณด์™„์ ์€ ์•ฝ์  ๊ณ ๋ฐฑ์ด ์•„๋‹ˆ๋ผ ์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ์œผ๋กœ ๋ณด์—ฌ์•ผ ํ•œ๋‹ค. + +```text +_______ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, _______์„ ํ†ตํ•ด ๋ณด์™„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. +``` + +์˜ˆ์‹œ: + +```text +๋Œ€๊ทœ๋ชจ ์šด์˜ ๊ฒฝํ—˜์€ ์•„์ง ๋ถ€์กฑํ•˜์ง€๋งŒ, ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์—์„œ ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•˜๋ฉฐ ์šด์˜ ๊ฐ๊ฐ์„ ๋ณด์™„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. +``` + +### ํ”ผํ•ด์•ผ ํ•  ๋ฌธ์žฅ + +```text +์—ด์‹ฌํžˆ ๋ฐฐ์šฐ๊ฒ ์Šต๋‹ˆ๋‹ค. +์ตœ์„ ์„ ๋‹คํ•ด ๊ธฐ์—ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. +๊ผญ ํ•„์š”ํ•œ ์ธ์žฌ๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. +ํšŒ์‚ฌ์™€ ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. +``` + +### ์ข‹์€ ๋งˆ๋ฌด๋ฆฌ ์˜ˆ์‹œ + +```text +์žฅ๊ธฐ์ ์œผ๋กœ๋Š” ์„œ๋น„์Šค ๊ตฌ์กฐ ๊ฐœ์„ ๊ณผ ๊ธฐ์ˆ  ๊ณต์œ ์— ๊ธฐ์—ฌํ•˜๋ฉฐ, ํŒ€์ด ์•ˆ์ •์ ์œผ๋กœ ํ™•์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ ๋ฌธํ™”๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ๋ณดํƒฌ์ด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. +``` + +--- + +## 3-4. ์ž๊ธฐ์†Œ๊ฐœ / ์„ฑ์žฅ ๊ณผ์ • + +### ํ•ต์‹ฌ ์งˆ๋ฌธ + +```text +๋‚˜๋Š” ์–ด๋–ค ๊ฐœ๋ฐœ์ž์ธ๊ฐ€? +``` + +### ์ž‘์„ฑ ๋ชฉํ‘œ + +๊ฐœ๋ฐœ ์ง๋ฌด์˜ ์ž๊ธฐ์†Œ๊ฐœ๋Š” ์–ด๋ฆฐ ์‹œ์ ˆ ์ด์•ผ๊ธฐ๋‚˜ ์„ฑ๊ฒฉ ์†Œ๊ฐœ๊ฐ€ ์•„๋‹ˆ๋‹ค. + +๊ฐœ๋ฐœ์ž๋กœ์„œ์˜ ์ •์ฒด์„ฑ์ด ์–ด๋–ป๊ฒŒ ํ˜•์„ฑ๋˜์—ˆ๋Š”์ง€ ๋ณด์—ฌ์ค˜์•ผ ํ•œ๋‹ค. + +### ๊ถŒ์žฅ ๊ตฌ์กฐ + +```text +โ‘  ๊ฐœ๋ฐœ์— ๊ด€์‹ฌ์„ ๊ฐ–๊ฒŒ ๋œ ๊ณ„๊ธฐ +โ‘ก ๊ธฐ์ˆ ์  ์„ฑ์žฅ์˜ ์ „ํ™˜์ ์ด ๋œ ๊ฒฝํ—˜ +โ‘ข ๊ทธ ๊ฒฝํ—˜์ด ํ˜„์žฌ์˜ ๊ฐœ๋ฐœ ๋ฐฉ์‹์— ์ค€ ์˜ํ–ฅ +โ‘ฃ ํ˜„์žฌ ๋‚˜์˜ ๊ฐœ๋ฐœ์ž ์ •์ฒด์„ฑ ์š”์•ฝ +``` + +### ์ข‹์€ ๋ฐฉํ–ฅ + +```text +๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋„˜์–ด ์œ ์ง€๋ณด์ˆ˜ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ +๋‹จ์ˆœ ๊ตฌํ˜„๋ณด๋‹ค ๋ฌธ์ œ ์›์ธ ๋ถ„์„์„ ์ค‘์š”ํ•˜๊ฒŒ ์—ฌ๊ธฐ๊ฒŒ ๋œ ๊ฒฝํ—˜ +ํ˜‘์—… ๊ณผ์ •์—์„œ ์ฝ”๋“œ ํ’ˆ์งˆ๊ณผ ๋ฌธ์„œํ™”์˜ ์ค‘์š”์„ฑ์„ ๋А๋‚€ ๊ฒฝํ—˜ +``` + +### ํ”ผํ•ด์•ผ ํ•  ๋‚ด์šฉ + +```text +์–ด๋ฆฐ ์‹œ์ ˆ ์ด์•ผ๊ธฐ +๊ฐ€์กฑ ํ™˜๊ฒฝ ์„ค๋ช… +์ปดํ“จํ„ฐ๋ฅผ ์ข‹์•„ํ•ด์„œ ์‹œ์ž‘ํ–ˆ๋‹ค๋Š” ํ‘œํ˜„ +MBTI๋‚˜ ์„ฑ๊ฒฉ ์œ ํ˜• ์ค‘์‹ฌ ์„ค๋ช… +์ง๋ฌด์™€ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š๋Š” ์ธ์ƒ ์„œ์‚ฌ +``` + +--- + +## 4. ๋ถ„๋Ÿ‰๋ณ„ ์ž‘์„ฑ ์ „๋žต + +| ๋ถ„๋Ÿ‰ | ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€ | ์‚ฌ๋ก€ ์ˆ˜ | ์ „๋žต | +|---|---:|---:|---| +| 500์ž | 1๊ฐœ | 1๊ฐœ ์••์ถ• | ๊ฒฐ๋ก  โ†’ ํ•ต์‹ฌ ์‚ฌ๋ก€ โ†’ ๊ฒฐ๊ณผ โ†’ ์—ฐ๊ฒฐ | +| 700์ž | 1๊ฐœ | 1๊ฐœ ์ƒ์„ธ | ๊ฒฐ๋ก  โ†’ STAR ์ „๊ฐœ โ†’ ์—…๋ฌด ์—ฐ๊ฒฐ | +| 1000์ž | 1~2๊ฐœ | 1~2๊ฐœ | ๋ฉ”์ธ ์‚ฌ๋ก€ ์ƒ์„ธ + ๋ณด์กฐ ์‚ฌ๋ก€ ๊ฐ„๋žต | + +๋ถ„๋Ÿ‰์ด ์งง์„์ˆ˜๋ก ๋ฉ”์‹œ์ง€๋ฅผ ์ค„์ธ๋‹ค. 700์ž ์ดํ•˜์—์„œ๋Š” ๊ฐ•์  2~3๊ฐœ๋ฅผ ๋‚˜์—ดํ•˜์ง€ ์•Š๋Š”๋‹ค. + +--- + +## 5. ๋ฌธ์ฒด ์ปจ๋ฒค์…˜ + +### 5-1. ๊ธฐ๋ณธ ๋ฌธ์ฒด + +- ๊ธฐ๋ณธ ๋ฌธ์ฒด๋Š” `~ํ–ˆ์Šต๋‹ˆ๋‹ค`, `~ํ•ฉ๋‹ˆ๋‹ค` ์กด๋Œ“๋ง์„ ์‚ฌ์šฉํ•œ๋‹ค. +- ๋ฌธ์ฒด๋Š” ๋ฌธ์„œ ์ „์ฒด์—์„œ ํ†ต์ผํ•œ๋‹ค. +- ์ง€๋‚˜์น˜๊ฒŒ ๊ฐ์ •์ ์ธ ํ‘œํ˜„๋ณด๋‹ค ํŒ๋‹จ๊ณผ ๊ทผ๊ฑฐ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์“ด๋‹ค. + +--- + +### 5-2. ๋ฌธ์žฅ ๊ธธ์ด + +- ํ•œ ๋ฌธ์žฅ์€ 60์ž ๋‚ด์™ธ๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค. +- 80์ž๋ฅผ ๋„˜์œผ๋ฉด ๋ฌธ์žฅ์„ ๋‚˜๋ˆˆ๋‹ค. +- ๊ธด ๋ฌธ์žฅ์€ `๋ฌธ์ œ ์ƒํ™ฉ โ†’ ํŒ๋‹จ โ†’ ํ–‰๋™ โ†’ ๊ฒฐ๊ณผ`๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค. + +๋‚˜์œ ์˜ˆ: + +```text +์ €๋Š” ํ”„๋กœ์ ํŠธ์—์„œ API ์‘๋‹ต์†๋„๊ฐ€ ๋А๋ ค์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ฟผ๋ฆฌ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์บ์‹œ๋ฅผ ์ ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. +``` + +์ข‹์€ ์˜ˆ: + +```text +ํ”„๋กœ์ ํŠธ์—์„œ API ์‘๋‹ต์†๋„๊ฐ€ ๋А๋ ค์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. +๋จผ์ € ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•ด ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. +์ดํ›„ ์ฟผ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  Redis ์บ์‹œ๋ฅผ ์ ์šฉํ•ด ์‘๋‹ต์†๋„๋ฅผ ์ค„์˜€์Šต๋‹ˆ๋‹ค. +``` + +--- + +### 5-3. 1์ธ์นญ ๋ฐ˜๋ณต ์ œํ•œ + +`์ €๋Š”`์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์žฅ์ด 3ํšŒ ์ด์ƒ ์—ฐ์†๋˜๋ฉด ์ˆ˜์ •ํ•œ๋‹ค. + +๋Œ€์ฒด ํ‘œํ˜„: + +```text +ํ•ด๋‹น ํ”„๋กœ์ ํŠธ์—์„œ๋Š”... +์ด ๊ณผ์ •์—์„œ... +๋ฌธ์ œ์˜ ์›์ธ์€... +๊ทธ ๊ฒฐ๊ณผ... +์ด ๊ฒฝํ—˜์„ ํ†ตํ•ด... +``` + +--- + +### 5-4. ์ ‘์†์‚ฌ ์‚ฌ์šฉ ์ œํ•œ + +`๊ทธ๋ฆฌ๊ณ `, `๋˜ํ•œ`, `๊ทธ๋ž˜์„œ`๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. + +๋ฌธ์žฅ ํ๋ฆ„ ์ž์ฒด๊ฐ€ ์›์ธ๊ณผ ๊ฒฐ๊ณผ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋„๋ก ๊ตฌ์„ฑํ•œ๋‹ค. + +--- + +## 6. ๊ธˆ์ง€ ํ‘œํ˜„ ๋ฐ ๋Œ€์ฒด ๋ฐฉ์‹ + +| ๊ธˆ์ง€ ํ‘œํ˜„ | ์ด์œ  | ๋Œ€์ฒด ๋ฐฉ์‹ | +|---|---|---| +| ์—ด์ •์„ ๊ฐ€์ง€๊ณ  | ์ฆ๋ช… ๋ถˆ๊ฐ€๋Šฅ | ์‹ค์ œ ํ–‰๋™๊ณผ ์ง€์† ๊ธฐ๊ฐ„ ์ œ์‹œ | +| ์ตœ์„ ์„ ๋‹คํ•ด | ๋ˆ„๊ตฌ๋‚˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ | ๊ตฌ์ฒด์  ์‹คํ–‰ ๊ณ„ํš ์ œ์‹œ | +| ์ธ์žฌ๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค | ์ถ”์ƒ์  | ์–ด๋–ค ์—ญํ• ์„ ํ• ์ง€ ๋ช…์‹œ | +| ๋ˆ„๊ตฌ๋ณด๋‹ค | ๋น„๊ต ๋ถˆ๊ฐ€๋Šฅ | ์‚ฌ๋ก€์™€ ๊ฒฐ๊ณผ๋กœ ์ฆ๋ช… | +| ๋‚จ๋‹ค๋ฅธ | ์ž๊ธฐ ๊ณผ์žฅ | ์ฐจ๋ณ„ํ™”๋œ ํ–‰๋™ ์„ค๋ช… | +| ์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ | ๋ฒ”์šฉ ํ‘œํ˜„ | ํšŒ์‚ฌ ๋ฐฉํ–ฅ๊ณผ ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ | +| 4์ฐจ ์‚ฐ์—…ํ˜๋ช… | ๊ณตํ—ˆํ•จ | ํšŒ์‚ฌ์˜ ๊ตฌ์ฒด ๊ธฐ์ˆ  ๋ฐฉํ–ฅ ์‚ฌ์šฉ | +| ์—…๊ณ„๋ฅผ ์„ ๋„ํ•˜๋Š” | ํšŒ์‚ฌ ์†Œ๊ฐœ๋ฌธ ๊ฐ™์Œ | ์‹ค์ œ ์„œ๋น„์Šค / ๊ธฐ์ˆ  / ์‚ฌ์—… ๊ทผ๊ฑฐ ์‚ฌ์šฉ | +| ์ปดํ“จํ„ฐ๋ฅผ ์ข‹์•„ํ•ด์„œ | ํ”ํ•œ ํ‘œํ˜„ | ๊ฐœ๋ฐœ ๋ฐฉ์‹์ด ๋ฐ”๋€ ๊ฒฝํ—˜ ์‚ฌ์šฉ | + +--- + +## 7. ์†Œ์ œ๋ชฉ ์ž‘์„ฑ ์ปจ๋ฒค์…˜ + +์†Œ์ œ๋ชฉ์€ ๋ฌธํ•ญ ์ œ๋ชฉ์„ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. + +### ์ข‹์€ ์†Œ์ œ๋ชฉ ์กฐ๊ฑด + +- 15์ž ๋‚ด์™ธ +- ํ•ต์‹ฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ณด์ž„ +- ๊ฐœ๋ฐœ์ž ์ •์ฒด์„ฑ์ด ๋“œ๋Ÿฌ๋‚จ +- ์ถ”์ƒ์ ์ด์ง€ ์•Š์Œ + +### ์ข‹์€ ์˜ˆ์‹œ + +| ๋ฌธํ•ญ | ์†Œ์ œ๋ชฉ ์˜ˆ์‹œ | +|---|---| +| ์ง€์›๋™๊ธฐ | ๊ฒฝํ—˜์ด ์—ฐ๊ฒฐ๋˜๋Š” ๊ณณ | +| ์ง€์›๋™๊ธฐ | ๊ธฐ์ˆ  ๋ฐฉํ–ฅ์—์„œ ์–ป์€ ํ™•์‹  | +| ์—…๋ฌด ๊ฐ•์  | ๋ฌธ์ œ๋ฅผ ๋๊นŒ์ง€ ์ถ”์ ํ•˜๋‹ค | +| ์—…๋ฌด ๊ฐ•์  | ์„ฑ๋Šฅ ๊ฐœ์„ ์œผ๋กœ ์ฆ๋ช…ํ•œ ์‹คํ–‰๋ ฅ | +| ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ | ์•ˆ์ •์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ•จ๊ป˜ | +| ์ž…์‚ฌ ํ›„ ํฌ๋ถ€ | ๊ตฌ์กฐ ๊ฐœ์„ ์œผ๋กœ ๊ธฐ์—ฌํ•˜๋‹ค | + +### ๋‚˜์œ ์˜ˆ์‹œ + +```text +์ง€์›๋™๊ธฐ +์ €์˜ ์žฅ์  +์ž…์‚ฌ ํ›„ ํฌ๋ถ€ +์—ด์‹ฌํžˆ ํ•˜๋Š” ๊ฐœ๋ฐœ์ž +๋Š์ž„์—†์ด ๋„์ „ํ•˜๋Š” ์ธ์žฌ +``` + +--- + +## 8. ๊ฒฝํ—˜ ์„ ํƒ ๊ธฐ์ค€ + +์ž๊ธฐ์†Œ๊ฐœ์„œ์— ์‚ฌ์šฉํ•  ๊ฒฝํ—˜์€ ์•„๋ž˜ ๊ธฐ์ค€์œผ๋กœ ๊ณ ๋ฅธ๋‹ค. + +| ๊ธฐ์ค€ | ์„ค๋ช… | +|---|---| +| ์ง๋ฌด ๊ด€๋ จ์„ฑ | ์ง€์› ์ง๋ฌด์˜ ์—…๋ฌด์™€ ์ง์ ‘ ์—ฐ๊ฒฐ๋˜๋Š”๊ฐ€ | +| ๋ฌธ์ œ์„ฑ | ๋‹จ์ˆœ ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ ํ•ด๊ฒฐํ•  ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋Š”๊ฐ€ | +| ๋‚ด ์—ญํ•  | ๋‚ด๊ฐ€ ๋งก์€ ํŒ๋‹จ๊ณผ ํ–‰๋™์ด ๋ถ„๋ช…ํ•œ๊ฐ€ | +| ๊ฒฐ๊ณผ | ์ˆ˜์น˜๋‚˜ ๋ณ€ํ™”๋กœ ๊ฒฐ๊ณผ๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€ | +| ํšŒ์‚ฌ ์—ฐ๊ฒฐ์„ฑ | ํšŒ์‚ฌ์˜ ๊ธฐ์ˆ  / ์„œ๋น„์Šค ๋ฐฉํ–ฅ๊ณผ ์—ฐ๊ฒฐ ๊ฐ€๋Šฅํ•œ๊ฐ€ | + +์šฐ์„ ์ˆœ์œ„: + +```text +ํšŒ์‚ฌ ์š”๊ตฌ ์—ญ๋Ÿ‰๊ณผ ๋งž๋Š” ๊ฒฝํ—˜ > ๊ธฐ์ˆ ์ ์œผ๋กœ ๊นŠ์€ ๊ฒฝํ—˜ > ๋‹จ์ˆœํžˆ ๊ทœ๋ชจ๊ฐ€ ํฐ ๊ฒฝํ—˜ +``` + +--- + +## 9. ์ž‘์„ฑ ์ „ ์งˆ๋ฌธ ๋ฆฌ์ŠคํŠธ + +์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์“ฐ๊ธฐ ์ „ ์•„๋ž˜ ์งˆ๋ฌธ์— ๋‹ตํ•œ๋‹ค. + +### ํšŒ์‚ฌ ์งˆ๋ฌธ + +```text +์ด ํšŒ์‚ฌ๋Š” ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๊ฐ€? +์ด ํšŒ์‚ฌ์˜ ํ•ต์‹ฌ ์„œ๋น„์Šค๋Š” ๋ฌด์—‡์ธ๊ฐ€? +์ด ํšŒ์‚ฌ์˜ ์ฃผ์š” ๊ณ ๊ฐ์€ ๋ˆ„๊ตฌ์ธ๊ฐ€? +์ฑ„์šฉ๊ณต๊ณ ์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ๊ธฐ์ˆ  / ์—ญ๋Ÿ‰ ํ‚ค์›Œ๋“œ๋Š” ๋ฌด์—‡์ธ๊ฐ€? +์ตœ๊ทผ ์‚ฌ์—… ๋ฐฉํ–ฅ์ด๋‚˜ ๊ธฐ์ˆ  ๋ฐฉํ–ฅ์€ ๋ฌด์—‡์ธ๊ฐ€? +``` + +### ๋‚˜์˜ ๊ฒฝํ—˜ ์งˆ๋ฌธ + +```text +์ด ํšŒ์‚ฌ์˜ ์š”๊ตฌ ์—ญ๋Ÿ‰๊ณผ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ๋Š” ๋ฌด์—‡์ธ๊ฐ€? +๊ทธ ํ”„๋กœ์ ํŠธ์—์„œ ์‹ค์ œ๋กœ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋Š” ๋ฌด์—‡์ธ๊ฐ€? +๋‚ด๊ฐ€ ์ง์ ‘ ํŒ๋‹จํ•˜๊ณ  ์‹คํ–‰ํ•œ ๋ถ€๋ถ„์€ ๋ฌด์—‡์ธ๊ฐ€? +๊ฒฐ๊ณผ๋ฅผ ์ˆ˜์น˜๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€? +์ด ๊ฒฝํ—˜์ด ์ž…์‚ฌ ํ›„ ์—…๋ฌด์— ์–ด๋–ป๊ฒŒ ์—ฐ๊ฒฐ๋˜๋Š”๊ฐ€? +``` + +--- + +## 10. ์ตœ์ข… ์ ๊ฒ€ ์ฒดํฌ๋ฆฌ์ŠคํŠธ + +### 10-1. ์ „์ฒด ๊ตฌ์กฐ + +- [ ] ๋ฌธํ•ญ๋ณ„ ์—ญํ• ์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋Š”๊ฐ€ +- [ ] ๊ฐ™์€ ์‚ฌ๋ก€๊ฐ€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐ˜๋ณต๋˜์ง€ ์•Š๋Š”๊ฐ€ +- [ ] ๋ชจ๋“  ๋ฌธํ•ญ์˜ ์ฒซ ๋ฌธ์žฅ์—์„œ ๊ฒฐ๋ก ์ด ๋ณด์ด๋Š”๊ฐ€ +- [ ] ํšŒ์‚ฌ๋ช…๊ณผ ์ง๋ฌด๋ช…์„ ๋ฐ”๊ฟ”๋„ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฒ”์šฉ ๋ฌธ์žฅ์ด ์—†๋Š”๊ฐ€ + +### 10-2. ํšŒ์‚ฌ ์—ฐ๊ฒฐ + +- [ ] ์ฑ„์šฉ๊ณต๊ณ  / ํ™ˆํŽ˜์ด์ง€ / ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ / ์„œ๋น„์Šค ์ •๋ณด๊ฐ€ ๋ฐ˜์˜๋˜์—ˆ๋Š”๊ฐ€ +- [ ] ํšŒ์‚ฌ ๋ฐฉํ–ฅ๊ณผ ๋‚ด ๊ฒฝํ—˜์ด ์—ฐ๊ฒฐ๋˜๋Š” ๋ฌธ์žฅ์ด ์žˆ๋Š”๊ฐ€ +- [ ] ํšŒ์‚ฌ ์นญ์ฐฌ๋งŒ ์žˆ๊ณ  ๋‚ด ๊ฒฝํ—˜์ด ๋น ์ง„ ๋ฌธ์žฅ์€ ์—†๋Š”๊ฐ€ + +### 10-3. ์‚ฌ๋ก€ ํ’ˆ์งˆ + +- [ ] ์—…๋ฌด ๊ฐ•์  ๋ฌธํ•ญ์— STAR ๊ตฌ์กฐ๊ฐ€ ์žˆ๋Š”๊ฐ€ +- [ ] ํŒ€์ด ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ ํ•œ ํ–‰๋™์ด ๋ช…ํ™•ํ•œ๊ฐ€ +- [ ] ๊ธฐ์ˆ ์  ํŒ๋‹จ์ด ๋“œ๋Ÿฌ๋‚˜๋Š”๊ฐ€ +- [ ] ๊ฒฐ๊ณผ์— ์ˆซ์ž ๋˜๋Š” ๋น„๊ต ํ‘œํ˜„์ด ์žˆ๋Š”๊ฐ€ + +### 10-4. ํ‘œํ˜„ ํ’ˆ์งˆ + +- [ ] `์—ด์ •`, `์ตœ์„ `, `์ธ์žฌ`, `๋ˆ„๊ตฌ๋ณด๋‹ค` ๊ฐ™์€ ๊ธˆ์ง€ ํ‘œํ˜„์ด ์—†๋Š”๊ฐ€ +- [ ] `์ €๋Š”`์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์žฅ์ด 3ํšŒ ์ด์ƒ ๋ฐ˜๋ณต๋˜์ง€ ์•Š๋Š”๊ฐ€ +- [ ] 80์ž๋ฅผ ๋„˜๋Š” ๋ฌธ์žฅ์ด ์—†๋Š”๊ฐ€ +- [ ] ์†Œ์ œ๋ชฉ์ด ๋ฌธํ•ญ ์ œ๋ชฉ ๋ฐ˜๋ณต์ด ์•„๋‹Œ๊ฐ€ +- [ ] ๋งž์ถค๋ฒ• ๊ฒ€์‚ฌ๋ฅผ ์™„๋ฃŒํ–ˆ๋Š”๊ฐ€ + +--- + +## 11. AI ์ž‘์„ฑ / ๋ฆฌ๋ทฐ์šฉ ์ง€์‹œ๋ฌธ + +์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ๋ฆฌ๋ทฐํ•˜๋Š” AI๋Š” ์•„๋ž˜ ์›์น™์„ ๋”ฐ๋ฅธ๋‹ค. + +```text +๋„ˆ๋Š” IT ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ ์ฝ”์น˜๋‹ค. +์ž๊ธฐ์†Œ๊ฐœ์„œ๋Š” ํšŒ์‚ฌ ๋ถ„์„ + ๋‚ด ๊ฒฝํ—˜ ์—ฐ๊ฒฐ ๊ตฌ์กฐ๋กœ ์ž‘์„ฑํ•œ๋‹ค. +์ถ”์ƒ์ ์ธ ์„ฑ๊ฒฉ ํ‘œํ˜„, ๊ฐํƒ„, ๋‹ค์ง๋ฌธ์„ ํ”ผํ•˜๊ณ  ๊ธฐ์ˆ  ๊ฒฝํ—˜๊ณผ ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •์„ ์ค‘์‹ฌ์œผ๋กœ ์“ด๋‹ค. +๊ฐ ๋ฌธํ•ญ์€ ๋‘๊ด„์‹์œผ๋กœ ์‹œ์ž‘ํ•œ๋‹ค. +์ง€์›๋™๊ธฐ๋Š” ํšŒ์‚ฌ ๊ฐ•์ ๊ณผ ๋‚ด ๊ฒฝํ—˜์˜ ์—ฐ๊ฒฐ์„ ์ค‘์‹ฌ์œผ๋กœ ์“ด๋‹ค. +์—…๋ฌด ๊ฐ•์ ์€ STAR ๊ตฌ์กฐ๋กœ ์ž‘์„ฑํ•˜๊ณ , Action๊ณผ Result๋ฅผ ๊ฐ€์žฅ ๊ตฌ์ฒด์ ์œผ๋กœ ์“ด๋‹ค. +์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ํšŒ์‚ฌ ๋ฐฉํ–ฅ, ์ดˆ๊ธฐ/1๋…„/3๋…„ ์„ฑ์žฅ ๊ณ„ํš, ๊ธฐ์—ฌ ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•œ๋‹ค. +๋ฌธ์žฅ์€ 60์ž ๋‚ด์™ธ๋ฅผ ๊ถŒ์žฅํ•˜๊ณ , 80์ž๋ฅผ ๋„˜์œผ๋ฉด ๋‚˜๋ˆˆ๋‹ค. +๊ธˆ์ง€ ํ‘œํ˜„์€ ๊ตฌ์ฒด์  ํ–‰๋™๊ณผ ๊ฒฐ๊ณผ๋กœ ๋Œ€์ฒดํ•œ๋‹ค. +ํšŒ์‚ฌ๋ช…๋งŒ ๋ฐ”๊ฟ”๋„ ํ†ตํ•˜๋Š” ๋ฒ”์šฉ ๋ฌธ์žฅ์€ ์ˆ˜์ •ํ•œ๋‹ค. +``` + +--- + +## 12. ์ถœ๋ ฅ ํ˜•์‹ ์ปจ๋ฒค์…˜ + +์ž๊ธฐ์†Œ๊ฐœ์„œ ์ดˆ์•ˆ ์ถœ๋ ฅ ์‹œ ๊ธฐ๋ณธ ํ˜•์‹์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. + +```markdown +## ๋ฌธํ•ญ๋ช… + +### ์†Œ์ œ๋ชฉ + +๋ณธ๋ฌธ +``` + +๋ฆฌ๋ทฐ ์ถœ๋ ฅ ์‹œ ๊ธฐ๋ณธ ํ˜•์‹์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค. + +```markdown +## ์ดํ‰ +- + +## ์ข‹์€ ์  +- + +## ์ˆ˜์ •์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„ +| ์œ„์น˜ | ๋ฌธ์ œ | ์ˆ˜์ • ๋ฐฉํ–ฅ | +|---|---|---| + +## ๊ฐœ์„  ์ดˆ์•ˆ + +## ์ตœ์ข… ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- [ ] ํšŒ์‚ฌ ์—ฐ๊ฒฐ +- [ ] ๊ฒฝํ—˜ ์ฆ๋ช… +- [ ] ์ˆ˜์น˜ ๊ฒฐ๊ณผ +- [ ] ์ž…์‚ฌ ํ›„ ๊ธฐ์—ฌ +- [ ] ๊ธˆ์ง€ ํ‘œํ˜„ ์ œ๊ฑฐ +``` + +--- + +## 13. ํ•œ ์ค„ ์›์น™ + +์ข‹์€ IT ๊ฐœ๋ฐœ์ž ์ž๊ธฐ์†Œ๊ฐœ์„œ๋Š” ๋‹ค์Œ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์ •๋ฆฌ๋œ๋‹ค. + +```text +ํšŒ์‚ฌ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ๋‚ด ๊ฒฝํ—˜์œผ๋กœ ์ฆ๋ช…ํ•˜๊ณ , ์•ž์œผ๋กœ์˜ ๊ธฐ์—ฌ ๊ณ„ํš๊นŒ์ง€ ์—ฐ๊ฒฐํ•œ ๊ธ€์ด๋‹ค. +``` +