<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dragon_jun.log</title>
        <link>https://velog.io/</link>
        <description>용용</description>
        <lastBuildDate>Wed, 02 Jul 2025 08:57:57 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dragon_jun.log</title>
            <url>https://velog.velcdn.com/images/dragon_jun/profile/b210e332-a502-4429-9089-d30fbacc9126/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dragon_jun.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dragon_jun" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[개인 프로젝트 1]]></title>
            <link>https://velog.io/@dragon_jun/%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1</link>
            <guid>https://velog.io/@dragon_jun/%EA%B0%9C%EC%9D%B8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1</guid>
            <pubDate>Wed, 02 Jul 2025 08:57:57 GMT</pubDate>
            <description><![CDATA[<p>리엑트+스프링부트+mysql 조합으로 하나 해보기로 마음먹었다.
리엑트과 스프링부트 따로따로 인강 들어봐서 복습하면서 연결할 생각</p>
<p>프로젝트 계획, 시스템 아키텍처, ERD, 기능정의서, API명세서 등등을 해봐야 하겠지만 실무에서 접해본 것이 아니라 너무 어렵다 ㅠㅠ
시작부터 힘들면 힘빠지니 일단 결과물 뽑아내는 걸로 하자 ! </p>
<h2 id="첫-프로젝트-목표">첫 프로젝트 목표</h2>
<ol>
<li>프론트(React)+백(SpringBoot) 분리</li>
<li>기본적인 CI/CD (git) </li>
<li>기본적인 CRUD (jpa+mysql)</li>
</ol>
<h2 id="두번쨰-프로젝트-목표">두번쨰 프로젝트 목표</h2>
<ol>
<li>프로젝트 계획서 만들기</li>
<li>Figma로 화면 디자인</li>
<li>외부 api연결</li>
<li>내가 만들고 싶은 사이트 만들기</li>
</ol>
<p>이직전까지 첫번쨰 완성, 두번쨰는 진행중으로 하는게 목표</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[구조 및 파일 역할]]></title>
            <link>https://velog.io/@dragon_jun/%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%ED%8C%8C%EC%9D%BC-%EC%97%AD%ED%95%A0</link>
            <guid>https://velog.io/@dragon_jun/%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%ED%8C%8C%EC%9D%BC-%EC%97%AD%ED%95%A0</guid>
            <pubDate>Thu, 24 Apr 2025 05:05:12 GMT</pubDate>
            <description><![CDATA[<p>buildDev.xml : 자바빌드, war파일 생성(패키징), 라이선스 복사 -&gt; 넥사크로+자바 완성
web.xml : 웹에서 실행될 시작 메뉴판
.jar : 자바에서 쓸 라이브러리 --&gt; war 배포시 자동 로드</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[점표기법/대괄호 표기법 - 자바스크립트]]></title>
            <link>https://velog.io/@dragon_jun/%EC%A0%90%ED%91%9C%EA%B8%B0%EB%B2%95%EB%8C%80%EA%B4%84%ED%98%B8-%ED%91%9C%EA%B8%B0%EB%B2%95-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@dragon_jun/%EC%A0%90%ED%91%9C%EA%B8%B0%EB%B2%95%EB%8C%80%EA%B4%84%ED%98%B8-%ED%91%9C%EA%B8%B0%EB%B2%95-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Wed, 23 Apr 2025 02:20:35 GMT</pubDate>
            <description><![CDATA[<p>점표기법 : 객체.속성
대괄호 표기법 : 객체[&quot;속성&quot;]</p>
<p>점표기법은 간단하고 직관적이어서 많이 쓰임
대괄호 표기법은 속성이 동적으로 바뀌거나 문자열일때 사용</p>
<p>기능은 같음</p>
<p>svcAddObj = new Object();//[공통] 서비스 추가 파라메타 정보 Object
svcAddObj.sDeleteSql = &quot;mm:M_MM_3100_M_DL11&quot;;//삭제Sql</p>
<p>var objSvc = objEnv.services[&quot;svcUrl&quot;]</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[공통값 접근]]></title>
            <link>https://velog.io/@dragon_jun/%EA%B3%B5%ED%86%B5%EA%B0%92-%EC%A0%91%EA%B7%BC</link>
            <guid>https://velog.io/@dragon_jun/%EA%B3%B5%ED%86%B5%EA%B0%92-%EC%A0%91%EA%B7%BC</guid>
            <pubDate>Wed, 23 Apr 2025 02:12:20 GMT</pubDate>
            <description><![CDATA[<h2 id="-로-매핑">:: 로 매핑</h2>
<p>Nexacro에서 svcUrl::과 같은 방식은 
Service URL을 매핑(alias) 방식
ex)
var svcUrl = &quot;svcUrl::XExportImport&quot;;
//var svcUrl = &quot;<a href="http://localhost.:8190/XExportImport&quot;">http://localhost.:8190/XExportImport&quot;</a>;</p>
<ul>
<li>저장 경로 : TypeDefinition&gt;Services</li>
</ul>
<h2 id="공통으로-정해놓은-파일에-접근">공통으로 정해놓은 파일에 접근</h2>
<p>ex) this.G_SV_RD;</p>
<ul>
<li>저장 경로 : typeDefinition&gt;ObjectsModuls에 .json추가&gt;json파일 내의 scripts에 파일(.js파일) 경로 추가&gt;파일에 공통값 정의<h2 id="공통으로-정해놓은-값에-접근">공통으로 정해놓은 값에 접근</h2>
</li>
</ul>
<p>App.gvSystem // ERP --&gt; gvSystem 이라는 id의 initval 값</p>
<ul>
<li>저장 경로 : Application Variables&gt;Variables
ex)</li>
</ul>
<h2 id="웹컴포넌트-연결">웹컴포넌트 연결</h2>
<p>ex)
this.web_ContNm.set_url(this.gfn_GetServerUrl(&quot;View&quot;));//윈도우 환경에서 웹에디터 Url 설정-View 
// this.gfn_GetServerUrl(&quot;View&quot;) : <a href="http://localhost:8190/ckeditor/webViewer.html">http://localhost:8190/ckeditor/webViewer.html</a> --&gt; 환경이 바뀌면 공통함수에 의해 경로 변경</p>
<p>this.web_ContNm.getProperty(&quot;window&quot;).callMethod(&quot;fn_updateSize&quot;, iHeight);
webViewer.html에 iHeight 넘기면서 미리 정의된 메서드인 fn_updateSize를  호출</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[예외처리]]></title>
            <link>https://velog.io/@dragon_jun/%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@dragon_jun/%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Tue, 15 Apr 2025 01:22:49 GMT</pubDate>
            <description><![CDATA[<p>스프링으로 되어있다. </p>
<h2 id="1-오류-던짐">1. 오류 던짐</h2>
<pre><code class="language-java">...
if (StringUtil.isNull(sInDataSet)) {
    //오류메세지 - [공통서비스] 입력 데이터셋 정보가 유효하지 않습니다.
    throw new BizException(-901, &quot;err.admn.autosvc.indataset.invalid&quot;);
}
...</code></pre>
<h2 id="2-오류-낚아챔--messagesource-주입">2. 오류 낚아챔 + MessageSource 주입</h2>
<pre><code class="language-java">@ExceptionHandler(BizException.class)     
    public Object handleBizException(HttpServletRequest request, Exception e) throws Exception {    

    @Autowired
    private MessageSource messageSource;  

    ...
    strMsg = messageSource.getMessage(msgCode, args, Locale.getDefault());
    ...
</code></pre>
<h2 id="3-bean-등록--messagesource-구현">3. Bean 등록 + MessageSource 구현</h2>
<pre><code class="language-java">@Bean
    public ReloadableResourceBundleMessageSource messageSource() {
        ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
        String classpath = System.getProperty(&quot;java.class.path&quot;);
        reloadableResourceBundleMessageSource.setBasenames(
            &quot;classpath:/egovframework/message/com/message-common&quot;,
            &quot;classpath:/org/egovframe/rte/fdl/idgnr/messages/idgnr&quot;,
            &quot;classpath:/org/egovframe/rte/fdl/property/messages/properties&quot;);
        reloadableResourceBundleMessageSource.setCacheSeconds(60);
        return reloadableResourceBundleMessageSource;
    }</code></pre>
<h3 id="설명">설명</h3>
<p>@ExceptionHandler 는 오류가 발생했을때 낚아챈다.</p>
<p>ReloadableResourceBundleMessageSource 라는 구현체를 @bean으로 등록해 놓고 MessageSource 에서 @autowired로 bean을 주입(의존성주입) 받는다. </p>
<p>결합도를 낮추고 유연하게 쓰자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JSON]]></title>
            <link>https://velog.io/@dragon_jun/JSON</link>
            <guid>https://velog.io/@dragon_jun/JSON</guid>
            <pubDate>Mon, 14 Apr 2025 07:52:16 GMT</pubDate>
            <description><![CDATA[<p>처음알았다
JSON 이라는게 2가지로 쓰임</p>
<ul>
<li>전역 객체<br>JSON.stringify()/JSON.parse()</li>
<li>형태 <pre><code class="language-json">{
  &quot;이름&quot;:&quot;용용&quot;,
  &quot;나이&quot;:&quot;비밀&quot;
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[넥사크로13 - 다른 화면 호출]]></title>
            <link>https://velog.io/@dragon_jun/%EB%84%A5%EC%82%AC%ED%81%AC%EB%A1%9C13-%EB%8B%A4%EB%A5%B8%ED%99%94%EB%A9%B4-%ED%98%B8%EC%B6%9C-10rxyq13</link>
            <guid>https://velog.io/@dragon_jun/%EB%84%A5%EC%82%AC%ED%81%AC%EB%A1%9C13-%EB%8B%A4%EB%A5%B8%ED%99%94%EB%A9%B4-%ED%98%B8%EC%B6%9C-10rxyq13</guid>
            <pubDate>Wed, 09 Apr 2025 05:47:12 GMT</pubDate>
            <description><![CDATA[<p>회사에서 운영중인 코드는 아니고
이렇게 바꾸면 어떨까 해서 수정해봄</p>
<p>화면 구현은 다른 화면들과 별반 다르지 않으므로 생략</p>
<h2 id="기능">기능</h2>
<p>일정등록 화면에서 일정등록시 메인 페이지의 일정관리 달력이 업데이트 되는 기능</p>
<h2 id="코드">코드</h2>
<pre><code class="language-javascript">- 일정등록 화면
    case &quot;SAVE&quot;:   //저장        
            this.fn_ComBtnSearch();//조회

            //일정등록 저장시 메인포탈 새로고침 및 포커스
            this.gfn_CallScrn(&quot;Form_Portal&quot;,&quot;fn_mainPotal_refresh&quot;,null,&quot;&quot;,false);
            break;    </code></pre>
<p>공통함수로 넘기는 파라미터 :
(호출할 화면,화면에서 동작시킬 함수,같이 넘길 데이터,메뉴 닫을시 이동시킬 화면)</p>
<p>저장했을때 메인화면을 새로고침 한다.</p>
<p>공통함수를 살펴보자 !</p>
<pre><code class="language-javascript">- Bizutil.js
pForm.gfn_CallScrn = function(sPrgmId, sCallFunc, oParam, sMoveScrnId)
{
    var sMenuId = nApp.gds_Menu.lookup(&quot;PRGM_ID&quot;, sPrgmId, &quot;MENU_ID&quot;);//메뉴ID
    var winId = nApp.gds_OpenMenu.lookup(&quot;MENU_ID&quot;, sMenuId, &quot;WIN_ID&quot;);
    var oObj = {
            menuId : sMenuId,  // 메뉴ID
            scrnCallFunc : sCallFunc, //화면간 호출함수명
            scrnCallArgs  : oParam        //화면간 전달 argument
        };    

    //선택된 메뉴 호출
    this.gfn_CallMenu(oObj);        

    //Tab 메뉴 닫을시 이동할 화면 설정
    if (!this.gfn_IsNull(sMoveScrnId)) {
        this.gfn_MoveScrn(sMoveScrnId);
    }

    if (!this.gfn_IsNull(winId) &amp;&amp; !this.gfn_IsNull(sCallFunc) ) {

        var framesInfo = nApp.gvWorkFrame.frames; //WorkFrame 정보
        var oCallScrn = framesInfo[winId];    
        oCallScrn.form.div_Work.form.lookupFunc(sCallFunc).call(oParam);//함수 호출    
    }

}</code></pre>
<p>특이했던 점은 유사배열을 썻다는 건데 
framesInfo[winId]
이건 index가 아닌 객체명으로 값에 접근할수 있다는 거였다.</p>
<p>lookupFunc는 넥사 내장함수로 함수 호출하는거고 
oCallScrn.form.div_Work.form.lookupFunc(sCallFunc) 로 함수 호출 후 
oParam 값을 넘겨 쓸수도 있다.</p>
<pre><code class="language-javascript">- 메인
//일정등록 저장시 메인포탈 일정관리 새로고침
this.fn_mainPotal_refresh = function()
{
    //this.gfn_Alert(&quot;SY134&quot;);    //저장되었습니다.

    //일정관리 일정 새로고침
    var fvDiv = this.tab_Portal.tpg_01.form.div_TopLeft.form;    
    var sDay = fvDiv.ds_Input.getColumn(0, &quot;S_DAY&quot;);    
    fvDiv.fvReSeaarch = true;//재조회 여부

    fvDiv.fn_ComBtnSearch();                //달력 새로고침
    fvDiv.fn_SelectSchedule(sDay);        //일정 새로고침
};
</code></pre>
<p>fvDiv는 화면안에 화면으로 구성되어 있다. 
fn_SelectSchedule는 모든 화면에 있는 조회하는 함수다.
경로가 잘 맞는지 잘 확인 해야한다. 보통 경로에서 오류 나드라</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 정리]]></title>
            <link>https://velog.io/@dragon_jun/React-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dragon_jun/React-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 08 Apr 2025 08:09:15 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-작업완료-내역">✅ 작업완료 내역</h2>
<ul>
<li>회원가입 및 로그인 기능 구현  </li>
<li>로그인 후 토큰 기반 세션 관리 - 새로고침 후에도 Redux-Persist로 상태 유지 (localStorage 사용)    </li>
<li>제품 리스트 조회  </li>
<li>제품 리스트에서 &quot;주문하기&quot; 클릭 시 장바구니(CART) 추가  </li>
<li>장바구니 내 수량 증가/감소/삭제 기능  </li>
<li>장바구니에서 행 추가 기능 구현  </li>
<li>장바구니에서 검색 기능 구현  </li>
<li>마이페이지(MyPage)에서 내 정보 확인 가능  </li>
</ul>
<hr>
<h2 id="✅-작업-계획">✅ 작업 계획</h2>
<ul>
<li><input disabled="" type="checkbox"> 백엔드 연결 </li>
<li><input disabled="" type="checkbox"> 깃 배포</li>
<li><input disabled="" type="checkbox"> UX/UI 개선</li>
<li><input disabled="" type="checkbox"> 추가 기능 구현 - 메일/세션관리/토큰/Spring Security/SLF4J/예외처리/파일다운로드/관리자/카카오로그인</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 로그인, 회원가입]]></title>
            <link>https://velog.io/@dragon_jun/React-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85</link>
            <guid>https://velog.io/@dragon_jun/React-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85</guid>
            <pubDate>Tue, 08 Apr 2025 07:26:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>처음에는 프로젝트가 조그맣고 귀여웠는데 점점 기능이 덕지덕지 붙으면서 무겁고 안귀여워짐.... </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dragon_jun/post/4d75f4db-cc17-4165-bd1a-dfdd3018e734/image.png" alt=""></p>
<p>사실 회원가입이야 로그인이랑 별반 다를바가 없어서 로그인만 정리함.</p>
<p>리덕스에 관리할게 많아지다 보니 slice들을 분리하기로함 
store 폴더안에 authSlice.js/cartSlice.js/shoesSlice.js/store.js 로 분리
권한도 Redux로 영속성화 시켜서 관리함 -&gt; 이거 때문에 어질어질 </p>
<h2 id="기능">기능</h2>
<ol>
<li>받은 아이디, 비밀번호 값을 밸리데이션 체크</li>
<li>아이디, 비밀번호 백단으로 넘김
(백단에서 확인됬다고 가정하고)</li>
<li>로그인시 토큰 발급 및 세션유지</li>
<li>상단 네비바에 버튼을 Login-&gt;Logout으로 변경 </li>
</ol>
<blockquote>
<p>이게 마지막인데 여기까지 오면서 모듈화하고 코드개선하고 폴더구조 정리하면서 이것저것 많이 바뀌었다. 밸로그 최신화가 필요하겠다. 
앞단이든 백단이든 로그인 기능처리를 할수있다면 기본은 갖춘게 아닐까 
챗지피티를 이용해서 개발하는데 너무 의존하고 있는건 아닌지 모르겠다. 하지만 챗지피티가 있으니 그나마 이정도 개발 할수 있는것도 맞다. 
어찌됫든 백단 강의 빨리 시작해야겠다. 
이직해야지</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 추가기능]]></title>
            <link>https://velog.io/@dragon_jun/React-%EC%B6%94%EA%B0%80%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@dragon_jun/React-%EC%B6%94%EA%B0%80%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Thu, 03 Apr 2025 06:55:38 GMT</pubDate>
            <description><![CDATA[<h2 id="기능">기능</h2>
<ol>
<li>memo</li>
<li>외부에서 정의한 컴퍼넌트 사용 및 외부 라이브러리 사용</li>
</ol>
<pre><code class="language-javascript">import { PlusAgeButton } from &quot;../components/button&quot;;
import { useState, memo, useEffect } from &#39;react&#39;;
import { Nav } from &#39;react-bootstrap&#39;    // 리엑트 부트스트랩 라이브러리
import { useLike } from &quot;../hooks/useLike.js&quot;;

function Func(){
    let [count, setCount] = useState(0)
    let [alert11, setAlert] = useState(true)
    let [탭, 탭변경] = useState(0)
    let [likeCount,likeCountFunc] = useLike()   // 외부에 정의해놓은 useLike.js 파일 임포트하고 내부함수 써먹기

    useEffect(()=&gt;{
        let a = setTimeout(()=&gt;{ setAlert(false) }, 2000)
        return ()=&gt;{    // 이거 먼저 실행
            clearTimeout(a)
        }
    }, [])

    return (
        &lt;div&gt;
            &lt;&gt;
            {// 팝업 알림창
                alert11 == true 
                ?&lt;div className=&quot;alert alert-warning&quot;&gt;
                    2초이내 구매시 할인
                &lt;/div&gt;
                : null
            }

            {/* 22살까지만 나이먹기 */}
            &lt;PlusAgeButton/&gt;

            &lt;br&gt;&lt;/br&gt;

            {/* 재랜더링 테스트 */}
            &lt;Child/&gt;
            &lt;button onClick={ () =&gt; {setCount(count+1)} }&gt;+1&lt;/button&gt;
            &lt;/&gt;

            {/* 탭변경 */}
            &lt;Nav variant=&quot;tabs&quot;  defaultActiveKey=&quot;link0&quot;&gt;
                &lt;Nav.Item&gt;
                    &lt;Nav.Link onClick={()=&gt;{탭변경(0)}} eventKey=&quot;link0&quot;&gt;버튼0&lt;/Nav.Link&gt;
                &lt;/Nav.Item&gt;
                &lt;Nav.Item&gt;
                    &lt;Nav.Link onClick={()=&gt;{탭변경(1)}} eventKey=&quot;link1&quot;&gt;버튼1&lt;/Nav.Link&gt;
                &lt;/Nav.Item&gt;
                &lt;Nav.Item&gt;
                    &lt;Nav.Link onClick={()=&gt;{탭변경(2)}} eventKey=&quot;link2&quot;&gt;버튼2&lt;/Nav.Link&gt;
                &lt;/Nav.Item&gt;
            &lt;/Nav&gt;

            &lt;TabContent 탭={탭}&gt;&lt;/TabContent&gt;   

            &lt;h4&gt;{likeCount}&lt;/h4&gt;
            &lt;button onClick={()=&gt;{likeCountFunc()}}&gt;❤️&lt;/button&gt;
        &lt;/div&gt;
    )
}

//필요한 경우에만 재랜더링 - memo
let Child = memo( function(){ // 부모(App)가 랜더링 되도 자식이 랜더링 되지 않음 - 부모가 count 관리하고 있음 
    // memo가 없으면 count 증가시 버튼 Refresh
    console.log(&#39;memo 쓰니 재랜더링 안돼지? memo가 없었으면 +1 눌렀을떄 &quot;재랜더링 테스트 - 자식임&quot; 이거 계속 나와야함&#39;)
    return &lt;div&gt;재랜더링 테스트 - 자식임&lt;/div&gt;
})

// let Child = ()=&gt;{
//   console.log(&#39;기본적으로 count가 증가하며 재랜더링이 되고 말아&#39;)
// }

function TabContent({탭}){
    let[fade, setFade] = useState(&#39;&#39;)
    useEffect(()=&gt;{
        setTimeout(() =&gt; {setFade(&#39;end&#39;)}, 100)
        return ()=&gt;{    
            setFade(&#39;&#39;)
        }    
    }, [탭])

    return (
        &lt;div className={&#39;start &#39;+fade}&gt; {/* App.css에 정의한 클래스 */}
            { [&lt;div&gt;0번째내용&lt;/div&gt;, &lt;div&gt;1번째내용&lt;/div&gt;, &lt;div&gt;2번째내용&lt;/div&gt;][탭] } 
        &lt;/div&gt;
    )    
}

export default Func;
</code></pre>
<pre><code class="language-javascript">//나이먹기 버튼
export function PlusAgeButton(){

    let [count, setCount] = useState(0);
    let [age, setAge] = useState(20);

    useEffect(()=&gt;{
        if(count&lt;3 &amp;&amp; count!=0){
        setAge(age+1)
        }  
    }, [count])    

    return(
        &lt;div&gt;
            &lt;div&gt;안녕하세요 전 {age} - 내 나이는 22에서 멈춤..  &lt;br/&gt;&#39;count는 증가해도 나이는 22살까지만 표시&#39;&lt;/div&gt;
            &lt;button onClick={()=&gt;{
                setCount(count+1)
                console.log(&#39;1살+ &#39;+count)
            }}&gt;누르면한살+&lt;/button&gt;
        &lt;/div&gt;
    )
}</code></pre>
<h3 id="memo">memo</h3>
<p>부모 컴퍼넌트가 리렌더링 되도 자식 컴퍼넌트가 리렌더링 되지 않게 할수 있음 즉 최적화 가능
+1 버튼을 눌렀을때 memo가 없다면 console.log(&#39;memo 쓰니 재랜더링 안돼지? memo가 없었으면 +1 눌렀을떄 &quot;재랜더링 테스트 - 자식임&quot; 이거 계속 나와야함&#39;) 이 값이 콘솔창에 계속 나와야 되는데 한번만 나오고 그다음부턴 안나옴 </p>
<h3 id="memo가-필요한-상항-">memo가 필요한 상항 :</h3>
<p>부모 컴포넌트가 자주 리렌더링될 때
부모의 state나 props가 변경되면 리렌더링되는데, memo를 사용하면 불필요한 자식 렌더링을 막을 수 있음</p>
<p>자식 컴포넌트가 무거운 연산을 수행할 때
예를 들어, API 요청을 하거나 무거운 계산을 수행하는 컴포넌트라면, 불필요한 렌더링을 막아 성능을 향상시킬 수 있음</p>
<p>나머진 노잼이라 걍 코드만 써놓음</p>
<h2 id="사용기술">사용기술</h2>
<ul>
<li>memo</li>
<li>외부 라이브러리 및 외부 컴퍼넌트 import</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 마이페이지]]></title>
            <link>https://velog.io/@dragon_jun/React-%EB%A7%88%EC%9D%B4%ED%8E%98%EC%9D%B4%EC%A7%80</link>
            <guid>https://velog.io/@dragon_jun/React-%EB%A7%88%EC%9D%B4%ED%8E%98%EC%9D%B4%EC%A7%80</guid>
            <pubDate>Thu, 03 Apr 2025 06:29:44 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dragon_jun/post/a2db0af6-d546-4ebc-9c1f-e311746783bf/image.png" alt=""></p>
<h2 id="기능">기능</h2>
<ol>
<li>비동기 사용(react-query)</li>
</ol>
<ul>
<li>수정 감지해서 실시간 데이터 업데이트</li>
</ul>
<ol start="2">
<li>비동기 랜더링시 로딩화면(Suspense)</li>
</ol>
<ul>
<li>비동기 화면에서 로딩중에 어떻게 보여줄지 만듬</li>
</ul>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom/client&#39;;
import { QueryClient, QueryClientProvider } from &#39;@tanstack/react-query&#39;; //실시간 데이터인 react-query
import { Provider } from &#39;react-redux&#39;;   //Redux
import { PersistGate } from &#39;redux-persist/integration/react&#39;;  //Redux 영속성
import { BrowserRouter } from &#39;react-router-dom&#39;; //라우터
import store, { persistor } from &#39;./store&#39;; // store.js에서 persistor import
import App from &#39;./App&#39;;

const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));

root.render(
  &lt;QueryClientProvider client={queryClient}&gt;    
    &lt;Provider store={store}&gt;
      &lt;PersistGate loading={null} persistor={persistor}&gt;
        &lt;BrowserRouter&gt;
          &lt;App /&gt;
        &lt;/BrowserRouter&gt;
      &lt;/PersistGate&gt;
    &lt;/Provider&gt;
  &lt;/QueryClientProvider&gt;
);
</code></pre>
<p>QueryClientProvider 요론걸 쓰겠다고 메인에 설정 먼저 하기</p>
<pre><code class="language-javascript">// 서버데이터 - 유저
import api from &quot;./axios_setting.js&quot;;
import { useQuery } from &#39;@tanstack/react-query&#39;;

const getUser = async () =&gt; {
    try {
        const response = await api.get(&#39;/userdata.json&#39;);
        return response.data;
    } catch (error) {
        console.error(&quot;Error fetching user:&quot;, error);
        throw error;
    }
};

export const User = () =&gt; {
    return useQuery({
        queryKey: [&quot;useData&quot;],        // 쿼리에 고유한 키 설정
        queryFn: getUser,             // 데이터 가져올 함수
        refetchOnWindowFocus: true,   // 창 활성화시 데이터 새로고침
        staleTime: 10000,             // 10초 동안 데이터 유지
        retry: 2,                     // 실패시 최대 2번 시도
        cacheTime: 10000,             // 케시된 데이터 10초 유지
    });
};
</code></pre>
<p>useQuery를 이용해서 가져온 데이터를 어떻게 쏘아줄지 디테일한 설정 가능</p>
<pre><code class="language-javascript">import { Table } from &#39;react-bootstrap&#39;;
import { User } from &quot;../services/axios_user&quot;;

function MyPage(){

    //서버에 User 정보 값
    const { data: user, isLoading, error } = User();  //data 값을 user 라는 이름으로 사용

    if (isLoading) return &#39;로딩중..&#39;;
    if (error) return &#39;에러발생..&#39;;

    return (
      &lt;div&gt;
        &lt;Table&gt;
          &lt;thead&gt;
            &lt;tr&gt;
                &lt;th&gt;사용자 이름&lt;/th&gt;
                &lt;th&gt;사용자 이메일&lt;/th&gt;
            &lt;/tr&gt;
          &lt;/thead&gt;

          &lt;tbody&gt;
            &lt;tr&gt;
              &lt;td&gt;{user.name}&lt;/td&gt;
              &lt;td&gt;{user.email}&lt;/td&gt;
            &lt;/tr&gt;
          &lt;/tbody&gt;

        &lt;/Table&gt;

      &lt;/div&gt;
    )

}    
export default MyPage;

</code></pre>
<p>const { data: user, isLoading, error } = User();
User 받아온 값인 data를 user라는 이름으로 사용할수도 있음 </p>
<pre><code class="language-javascript">{/* MyPage 이동 */}
            &lt;Route path=&quot;/myPage&quot; element={ 
                // 비동기작업 로딩중 보여주는 Suspense
                &lt;Suspense fallback ={&lt;div&gt;로딩중....&lt;/div&gt;}&gt;    
                    &lt;MyPage/&gt; 
                &lt;/Suspense&gt;
            } /&gt;</code></pre>
<p>서스펜스는 간단함</p>
<p>라우트가 있는곳에 어떻게 보여줄지 만들주면 끝 ~</p>
<h2 id="사용기술">사용기술</h2>
<ul>
<li>react-query</li>
<li>Suspense</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 디테일,카트 페이지]]></title>
            <link>https://velog.io/@dragon_jun/React-%EB%94%94%ED%85%8C%EC%9D%BC-%EC%B9%B4%ED%8A%B8</link>
            <guid>https://velog.io/@dragon_jun/React-%EB%94%94%ED%85%8C%EC%9D%BC-%EC%B9%B4%ED%8A%B8</guid>
            <pubDate>Mon, 31 Mar 2025 05:38:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dragon_jun/post/d762a0be-757a-4e16-95a1-4e2f29b5660b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dragon_jun/post/45a3d40c-1ca8-4284-8477-9b5be0b89dde/image.png" alt=""></p>
<h2 id="기능">기능</h2>
<ol>
<li>Detail에 있는 상품에 주문하기 버튼을 누르면 Cart 화면에 가져와서 뿌려줌</li>
<li>Cart에 이미 상품이 있을 경우 count를 +1 </li>
</ol>
<p>그런데 URL 변경시 새로고침이 되면서 Redux에 state가 날라가는 이슈가 있었다. 
그래서 redux-persist 기능으로 선택값을 Local Storage에 저장하여 관리하고 새로고침 버튼을 누르면 state값을 초기화 하는 기능을 구현했다. </p>
<p>Redux 이슈가 해결이 안되서 1주일 동안 고통받음..</p>
<p>Detail화면에 탭 변경,타이머,인풋 등의 useEffect를 사용해서 만드는 기능들도 만들었지만 이건 간단한 기능들이라 기록 안할래
(외부 라이브러리로 import해서 쓰거나 .css 에 클래스 만들어서 import해서 쓰거나 하면 되는듯)</p>
<blockquote>
<p>Detail.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { useParams } from &#39;react-router-dom&#39;; //라우터로 받은 데이터를 사용하기 위해 불러옴
import &quot;../App.css&quot;;  // css 파일
import { useSelector } from &#39;react-redux&#39;;
import { useNavigate } from &#39;react-router-dom&#39;  // 화면전환할때 쓰는 라우터 라이브러리
import SearchInput from &#39;../components/Input.jsx&#39;;
import { SearchList } from &#39;../components/button.jsx&#39;;
import { DetailFind } from &#39;./DetailFind.jsx&#39;;
import { useState  } from &#39;react&#39;;

function DetailPage(){

    let {id} = useParams(); // url로 받은 데이터를 화면에 이동에 따라 동적으로 인식하기 위해 id값을 받아옴
    let shoes = useSelector((state) =&gt; state.shoes);    // shoes 값은 Redux로 관리되고 있음
    let 찾은상품 = shoes.find((x) =&gt; x.id == id);   // Redux의 id가 라우터로 받은 id와 같은 &#39;찾은상품&#39; 찾기 
    let navigate = useNavigate()  //navigate는 url 이동시 새로고침 안해 그래서 써
    let [value, setValue] = useState(&#39;&#39;); //input에 들어갈 value값을 useState로 관리
    let [filteredShoes, setFilteredShoes] = useState(null); //input에 들어갈 value값을 useState로 관리

    return (
        &lt;&gt;
        { 찾은상품 ? 
            (
            // 특정상품 주문하기
            &lt;DetailFind 찾은상품={찾은상품}/&gt;
            ) : (
            &lt;div className=&quot;container&quot;&gt;
                &lt;h1&gt;* 상품 리스트 *&lt;/h1&gt;
                &lt;br&gt;&lt;/br&gt;
                &lt;SearchInput value={value} setValue={setValue} /&gt; {/* input 밸리데이션 체크후 아래로 value값 리턴 */}
                &lt;SearchList 
                    setFilteredShoes={setFilteredShoes}
                    value={value}
                    shoes={shoes}
                /&gt; {/* 상품 찾기 버튼 */}
                &lt;br/&gt;
                &lt;br/&gt;
                &lt;div className=&quot;row&quot;&gt;
                    {(filteredShoes &amp;&amp; filteredShoes.length === 0) ? (
                        &lt;p&gt;검색된 상품이 없습니다.&lt;/p&gt;
                    ) : (
                        (filteredShoes ?? shoes).map((product) =&gt; (// ?? : filteredShoes가 null 또는 undefined일 경우 shoes로 대체
                            &lt;div className=&quot;col-md-4&quot; key={product.id}&gt;
                                &lt;h4&gt;{product.title}&lt;/h4&gt;
                                &lt;p&gt;{product.content}&lt;/p&gt;
                                &lt;p&gt;{product.price}&lt;/p&gt;
                                {/* &lt;button className=&quot;btn btn-danger&quot; onClick={()=&gt;navigate(&#39;/detail/&#39;+product.id)} &gt;주문하기&lt;/button&gt;  */}
                                &lt;button className=&quot;btn btn-danger&quot; onClick={()=&gt;{
                                    navigate(&#39;/detail/&#39;+product.id) 
                                }} &gt;주문하기&lt;/button&gt; 
                            &lt;/div&gt;
                        ))
                    )}
                &lt;/div&gt;
            &lt;/div&gt;
            )
        }
        &lt;/&gt;
    )
}

export default DetailPage</code></pre>
<blockquote>
<p>DeatailFind.jsx</p>
</blockquote>
<pre><code class="language-javascript">// Detail 화면에서 상품을 찾았을 때 개별 상품을 보여주는 컴포넌트
import { MoveCart, MoveDetailPageButton, OrderToList } from &quot;../components/button&quot;

export function DetailFind({찾은상품}){ 

    return (
        &lt;div className=&quot;container&quot;&gt;
            &lt;div className=&quot;row&quot;&gt;

                &lt;div className=&quot;col-md-6&quot;&gt;
                    {/* &lt;img src={&#39;https://codingapple1.github.io/shop/shoes&#39;+(찾은상품.id+1)+&#39;.jpg&#39;} width=&quot;100%&quot; /&gt; */}
                &lt;/div&gt;

                &lt;div className=&quot;col-md-6&quot;&gt;
                    &lt;h2&gt;찾은 상품&lt;/h2&gt;
                    &lt;h4 className=&quot;pt-5&quot;&gt;{찾은상품.title}&lt;/h4&gt;
                    &lt;p&gt;{찾은상품.content}&lt;/p&gt;
                    &lt;p&gt;{찾은상품.price}&lt;/p&gt;
                    &lt;OrderToList item={{ id: 찾은상품.id, name: 찾은상품.title }} /&gt;
                &lt;/div&gt;

                {/* 디테일 All 페이지로 이동버튼 */}
                {&lt;MoveDetailPageButton/&gt;}
                &lt;br/&gt;

                {/* 장바구니로 이동버튼 */}
                {&lt;MoveCart/&gt;}

            &lt;/div&gt;
        &lt;/div&gt; 
    )
}</code></pre>
<blockquote>
<p>Cart.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { Table } from &#39;react-bootstrap&#39;;
import { useSelector, useDispatch } from &#39;react-redux&#39;; //context api 보다 설정이 복잡하지만 성능 최적화 가능
import { addCount, addItem } from &#39;../store.js&#39;;

function Cart() { 
  // Redux store에서 cart 상태를 가져옴
  let cartList = useSelector((state) =&gt; state.cart); // Redux store에서 cart 상태 가져오기
  let shoes = useSelector((state) =&gt; state.shoes); // Redux store에서 shoes 상태 가져오기
  let dispatch = useDispatch(); //전달함수

  //행추가
  const addNewItem = () =&gt; {
    const newItem = {id: Date.now(), name:&#39;Song&#39;, count:1}
    console.log(&#39;행추가 == &#39;+JSON.stringify(newItem))
    // const newItem = {id: state.cart.length, name:Name, count:1} --&gt; 입력값 받아서 저장하는 걸로 구현해보기
    dispatch(addItem(newItem));
  }

  return (
    &lt;div&gt;
      &lt;Table&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;상품번호&lt;/th&gt;
            &lt;th&gt;이름&lt;/th&gt;
            &lt;th&gt;수량&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;

        &lt;tbody&gt;
        {
          !cartList.cart ? (
            &lt;tr&gt;
              &lt;td colSpan=&quot;5&quot;&gt;값없음&lt;/td&gt;
            &lt;/tr&gt;
          ) : (
            cartList.cart.map((a, i) =&gt; (
              &lt;tr key={i}&gt;
                &lt;td&gt;{a.id}&lt;/td&gt;
                &lt;td&gt;{a.name}&lt;/td&gt;
                &lt;td&gt;{a.count}&lt;/td&gt;
                &lt;td&gt;
                  &lt;button onClick={() =&gt; dispatch(addCount(a.id))}&gt;+&lt;/button&gt;
                &lt;/td&gt;
              &lt;/tr&gt;
            ))
          )
        }
        &lt;/tbody&gt;
      &lt;/Table&gt;

      &lt;button onClick={addNewItem}&gt;행추가&lt;/button&gt;

    &lt;/div&gt;
  )

}    
export default Cart;</code></pre>
<p>Cart.jsx 에선 Detail.jsx에서 가져온 상품값을 map으로 뿌림</p>
<blockquote>
<p>store.js</p>
</blockquote>
<pre><code class="language-javascript">// Redux 라이브러리 사용 - state를 중앙에서 관리
import { configureStore } from &#39;@reduxjs/toolkit&#39;;
import { persistStore, persistReducer } from &#39;redux-persist&#39;; // redux-persist 관련 import
import { persistedCartReducer } from &#39;./cartSlice.js&#39;;
import shoesReducer from &#39;./shoesSlice.js&#39;
import { persistedAuthReducer } from &#39;./authSlice.js&#39;;

// 🟢 Store 생성
const store = configureStore({
  reducer: {
    cart: persistedCartReducer, // cart는  default 가 아닌 이름으로 보냈기에 작명한 그대로 써야함
    shoes: shoesReducer,        // shoes는 default 로 보냈기에 {} 안에 쓰지않고 이름을 마음대로 지을수 있음  
    auth : persistedAuthReducer, 
  },
});

// 🟢 persistor 생성
export const persistor = persistStore(store);
export default store;

</code></pre>
<blockquote>
<p>cartSlice.js</p>
</blockquote>
<pre><code class="language-javascript">import { configureStore, createSlice } from &#39;@reduxjs/toolkit&#39;;
import { persistStore, persistReducer } from &#39;redux-persist&#39;; // redux-persist 관련 import
import storage from &#39;redux-persist/lib/storage&#39;; // 로컬 스토리지 사용

// 🟢 Cart Slice
let cartSlice = createSlice({
    name: &#39;cart&#39;,
    initialState: {
      cart: []  // cart를 배열로 감싸서 초기화
    },
    reducers: {

      // cart에 수량 추가
      addCount(state, action) {
        let 번호 = state.cart.findIndex((a) =&gt; a.id === action.payload);
        if (번호 !== -1) {  //값이 없을시 index 값으로 -1 반환
          state.cart[번호].count++;
        }
      },

      // cart에 수량 감소
      deleteCount(state, action) {
        let 번호 = state.cart.findIndex((a) =&gt; a.id === action.payload);
        if (번호 !== -1) {  //값이 없을시 index 값으로 -1 반환
          state.cart[번호].count--;
        }
      },

      // 선택한 상품 Cart에 추가
      addItem(state, action) {
        let found = state.cart.find((item) =&gt; item.id === action.payload.id);
        if (found) {  // 같은 상품이 있을 경우 수량 증가
          found.count++; 
        } else {  // 같은 상품이 없을경우 상품 추가
          state.cart.push({ ...action.payload, count: 1 });
          console.log(&#39;추가&#39;)
        }
      },

      // 선택한 상품 Cart에서 삭제
      deleteItem(state, action) {
        let cartIndex = state.cart.findIndex((a) =&gt; a.id === action.payload);
        if (cartIndex !== -1) {
          state.cart.splice(cartIndex, 1);  // 해당 index의 요소 삭제
          console.log(&#39;삭제 완료&#39;);
        }
      },

      // cart 초기화
      clear(state){
        state.cart = []; // cart를 빈 배열로 초기화
      },
    },
  });

  // 🟢 persistConfig 설정
const persistConfig = {
    key: &#39;cart&#39;, // localStorage에서 저장할 key 값
    storage, // 로컬 스토리지 사용
    whitelist: [&#39;cart&#39;], // 영속화할 상태 목록 (cart만 저장)
  };

  export const persistedCartReducer = persistReducer(persistConfig, cartSlice.reducer);
  export const { addCount, deleteCount, addItem, deleteItem, clear } = cartSlice.actions;
</code></pre>
<blockquote>
<p>main.jsx</p>
</blockquote>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom/client&#39;;
import { QueryClient, QueryClientProvider } from &#39;@tanstack/react-query&#39;; //실시간 데이터인 react-query
import { Provider } from &#39;react-redux&#39;;   //Redux
import { PersistGate } from &#39;redux-persist/integration/react&#39;;  //Redux 영속성
import { BrowserRouter } from &#39;react-router-dom&#39;; //라우터
import store from &#39;./store/store&#39;;
import { persistor } from &#39;./store/store&#39;;
import App from &#39;./App&#39;;

const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));

root.render(
  &lt;QueryClientProvider client={queryClient}&gt;    
    &lt;Provider store={store}&gt;
      &lt;PersistGate loading={null} persistor={persistor}&gt;
        &lt;BrowserRouter&gt;
          &lt;App /&gt;
        &lt;/BrowserRouter&gt;
      &lt;/PersistGate&gt;
    &lt;/Provider&gt;
  &lt;/QueryClientProvider&gt;
);
</code></pre>
<h2 id="크롬-확장-프로그램">크롬 확장 프로그램</h2>
<p>우선 크롬 확장프로그램 Redux DevTools 이거 다운받기
<a href="https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko">https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ko</a></p>
<p>F12&gt;Redux&gt;prefetch 에 state가 어떻게 관리되는지 나옴</p>
<h2 id="redux-이슈">Redux 이슈</h2>
<p>shoes 값은 마운트 되면서 axios로 가지고 오기 때문에 언제나 redux 안에서 관리하지만
cart 값은 화면에서 내가 선택한 값이기에 따로 저장할 방식(redux-persist)과 공간(Local Storage)이 필요하다.</p>
<p>가장 시간을 많이 잡아먹은 이슈 내용은 store.js 로 넘어온 state.cart 값이 객체라는 것이다. map으로 뿌려줄 거기 때문에 배열로 바꿔야 한다.</p>
<h2 id="etc">etc</h2>
<h3 id="배열-구조-분해-할당">배열 구조 분해 할당</h3>
<pre><code class="language-javascript">let [likeCount,likeCountFunc] = useLike()</code></pre>
<p>요게 처음에 뭔가 싶었는데 &#39;배열 구조 분해 할당&#39; 라고 함
useLike() 함수의 return 값을 배열로 리턴함 그걸 배열로 받아서 선언하는것
이래 놓고 likeCount, likeCountFunc 그냥 쓰면 됨 </p>
<h3 id="널-병합-연산자">널 병합 연산자</h3>
<pre><code class="language-javascript">(filteredShoes ?? shoes).map((product) =&gt; (</code></pre>
<p>요론 문법도 있다
?? : filteredShoes가 null 또는 undefined일 경우 shoes로 대체</p>
<h2 id="사용기술">사용기술</h2>
<ul>
<li>redux</li>
<li>redux-persist</li>
<li>Router</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 메인페이지]]></title>
            <link>https://velog.io/@dragon_jun/React-%EB%A9%94%EC%9D%B8%ED%8E%98%EC%9D%B4%EC%A7%80</link>
            <guid>https://velog.io/@dragon_jun/React-%EB%A9%94%EC%9D%B8%ED%8E%98%EC%9D%B4%EC%A7%80</guid>
            <pubDate>Tue, 25 Mar 2025 00:52:54 GMT</pubDate>
            <description><![CDATA[<h2 id="화면">화면</h2>
<p><img src="https://velog.velcdn.com/images/dragon_jun/post/9926ee41-33e0-433c-ad47-31e9622319f8/image.png" alt=""></p>
<h2 id="기능">기능</h2>
<p>메인페이지로 기능은 3가지</p>
<ol>
<li>가져온 데이터 뿌리기</li>
<li>주문하기 버튼 클릭시 디테일 화면으로 </li>
<li>추가상품번호 클릭시 데이터 추가</li>
</ol>
<blockquote>
<p>Home.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { useSelector } from &#39;react-redux&#39;;
import Card from &#39;../components/Card.jsx&#39;;
import { AddShoesList } from &#39;../components/button.jsx&#39;;

function Home(){

    const shoes = useSelector((state) =&gt; state.shoes); // Redux에서 shoes 가져오기

    return(
        &lt;div className=&quot;container&quot;&gt;
            &lt;div className=&quot;row&quot;&gt;
            {shoes.map((a, i)=&gt;{ // map으로 반복문 돌리기~
                return &lt;Card shoes={a} i={i} key={a.id} &gt;&lt;/Card&gt;  // 만들어둔 Card 컴포넌트 사용하기
            })}
            {/* 추가상품버튼 */}
            &lt;AddShoesList shoes={shoes} /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    )
}

export default Home</code></pre>
<p>Home에선 호출만 한다.</p>
<p>&lt; Card &gt; 로 자식 컴포넌트를 만들고 props 형태로 shoes를 전달하여 실제로는 Card.jsx에 구현했다. </p>
<p>map은 그냥 반복문으로 뿌려주는 역할</p>
<blockquote>
<p>Card.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { useNavigate } from &#39;react-router-dom&#39;  // 화면전환할때 쓰는 라우터 라이브러리

// 컴포넌트 만들기
function Card(props) {  //위에서 쓸 컴포넌트 미리 만들어두기 
  let navigate = useNavigate()

  return (
    &lt;div className=&quot;col-md-4&quot;&gt;
      {/* &lt;img src={&#39;https://codingapple1.github.io/shop/shoes&#39; + (props.i+1) + &#39;.jpg&#39;} width=&quot;80%&quot; /&gt; */}
      &lt;h4&gt;{props.shoes.title}&lt;/h4&gt;
      &lt;p&gt;{props.shoes.price}&lt;/p&gt;
      &lt;button className=&quot;btn btn-danger&quot; onClick={()=&gt;navigate(&#39;/detail/&#39;+props.shoes.id)} &gt;주문하기&lt;/button&gt; 
    &lt;/div&gt;
  )
}

export default Card</code></pre>
<h4 id="navigate">navigate()</h4>
<p>useNavigate 훅을 통해 사용했고 컴퍼넌트 내부에서만 사용 가능하며 전체 페이지 새로고침 없이 동적이 Url 이동이 가능하게 함 </p>
<blockquote>
<p>App.jsx </p>
</blockquote>
<pre><code class="language-javascript">import &#39;./App.css&#39;;
import &#39;bootstrap/dist/css/bootstrap.min.css&#39;;
import styled from &#39;styled-components&#39;;  // 스타일드 컴포넌트 라이브러리- 컴포넌트 만들시 스타일 미리 주입 - 편하나 관리가 어려움
import { HomeNavbar } from &#39;./components/Navbar.jsx&#39;; //네비
import MyRoutes from &#39;./routes/Routes.jsx&#39;;
import useReload from &#39;./hooks/useReload.js&#39;;

function App() {

  // 화면 새로고침시 cart에 담아놓은 값 초기화 
  useReload();

  return (
    &lt;div&gt;
      {/* 홈 - 네비바 */}
      &lt;HomeNavbar/&gt;

      {/* 메인페이지 - 라우터 */}
      &lt;MyRoutes/&gt;

    &lt;/div&gt;
  )
}

export default App</code></pre>
<p>임포트는 여러군데에서 했지만 메인화면 코드는 이게 끝임 </p>
<p>여기에는 context api, useState  기능이 사용됨</p>
<p>useState를 이용해 json형태로 가지고온 shoes를 라우터에 보낸 후 화면으로 보냄</p>
<h3 id="usestate">useState</h3>
<p>useState는 리엑트에서 제공하는 hook 방법
예를 들어 </p>
<pre><code class="language-javascript">let [shoes, setShoes] = useState(data);</code></pre>
<p>값의 상태를 관리하기 위함</p>
<p>shoes는 데이터가 담긴 변수명
setShoes는 데이터를 수정할 함수명
data는 초기값
data에 값을 shoes에 넣고 이 값을 수정할땐 setShoes를 사용한다. </p>
<p>예를 들면 AddShoesList 버튼</p>
<blockquote>
<p>button.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { getList } from &#39;../services/axios_list&#39;;

//상품추가 버튼
export function AddShoesList({shoes, setShoes}){ //객체 구조 분해 할당 사용 ({}) 활용
//서버에서 데이터 가지고오기 - axios 라이브러리 사용 JQuery 같은거 
    const fetchShoes = async () =&gt; {
        try {
            const data = await getList();
            // 기존 shoes와 서버에서 가져온 데이터를 합침
            const updatedShoes = [...shoes, ...data];
            setShoes(updatedShoes);
        } catch (error) {
            console.error(&quot;상품 데이터를 불러오는 중 오류 발생:&quot;, error);
        }
    };

    //services에 구현하자
    return(
        &lt;button onClick={fetchShoes}&gt;추가상품 버튼&lt;/button&gt;    
    )
}</code></pre>
<p>getList 같은 경우는 axios로 값을 들고왔는데 이것도 한번 알아보자</p>
<blockquote>
<p>axios_list</p>
</blockquote>
<pre><code class="language-javascript">// 서버데이터 - 리스트
import api from &quot;./axios_setting.js&quot;;

export const getList = async ()=&gt;{
    try {
        const response = await api.get(&#39;/shop/data2.json&#39;);
        return response.data;
    } catch (error) {
        console.error(&quot;Error fetching user:&quot;,error);
        throw error;
    }
}
</code></pre>
<p>간단하다
그럼 axios 공통세팅도 알아볼까</p>
<blockquote>
<p>axios_user.js</p>
</blockquote>
<pre><code class="language-javascript">import axios from &quot;axios&quot;;

const api = axios.create({
    baseURL: &quot;https://codingapple1.github.io&quot;,   //기본url
    // timeout: 5000,  //시간제한
    // headers: {
    //     &quot;Content-Type&quot;:&quot;application.json&quot;    
    // },
});

// 요청 인터셉터(토큰추가)
// api.interceptors.request.use(
//     (config)=&gt;{
//         const token = localStorage.getItem(&quot;token&quot;); //토큰가져오기
//         if(token){
//             config.headers.Authorization = &#39;Bearer ${token}&#39;;
//         }
//         return config;
//     },
//     (error)=&gt;Promise.reject(error)
// );

//응답 인터셉터(에러처리)
api.interceptors.response.use(
    (response)=&gt;response,
    (error) =&gt; {
        console.error(&quot;API Error:&quot;,error);
        return Promise.reject(error);
    }
);

export default api;</code></pre>
<p>여기서 이것저것 환경설정 해주면 되겠다.</p>
<p>나중에 마이페이지 에서 axios + react-query 로 실시간 데이터 불러오는것도 정리할 예정</p>
<h3 id="props-vs-context-api-vs-redux">props VS Context api VS Redux</h3>
<p>props : 부모 -&gt; 자식 컴포넌트로 데이터 전달
Context api : 간단한 전역 상태 관리 
Redux : 복잡한 전역 상태 관리</p>
<h2 id="사용기술">사용기술</h2>
<ul>
<li>props</li>
<li>Context Api</li>
<li>useState</li>
<li>navigate</li>
<li>Component</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 네비 + 라우팅]]></title>
            <link>https://velog.io/@dragon_jun/React-%EB%84%A4%EB%B9%84</link>
            <guid>https://velog.io/@dragon_jun/React-%EB%84%A4%EB%B9%84</guid>
            <pubDate>Mon, 24 Mar 2025 07:46:00 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dragon_jun/post/77845320-0c49-4910-a823-3b65da170b61/image.png" alt=""></p>
<p>네비는 맨위에 위치하고 각 화면으로 이동시킴
디자인 : react-bootstrap
이동 : 라우팅</p>
<p>컴퍼넌트화를 통해 각 화면은 각 화면용으로 따로 구현</p>
<blockquote>
<p>Navbar.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { Navbar, Container, Nav} from &#39;react-bootstrap&#39;    // 리엑트 부트스트랩 라이브러리

export function HomeNavbar(){
    return(
        &lt;div&gt;
            &lt;Navbar bg=&quot;dark&quot; variant=&quot;dark&quot;&gt; 
            &lt;Container&gt;
            &lt;Navbar.Brand href=&quot;/home&quot;&gt;ShoesShop&lt;/Navbar.Brand&gt;
            &lt;Nav className=&quot;me-auto&quot;&gt;
                &lt;Nav.Link href=&quot;/home&quot;&gt;Home&lt;/Nav.Link&gt;
                &lt;Nav.Link href=&quot;./detail&quot;&gt;Detail&lt;/Nav.Link&gt;
                &lt;Nav.Link href=&quot;./cart&quot;&gt;Cart&lt;/Nav.Link&gt;
                &lt;Nav.Link href=&quot;./myPage&quot;&gt;MyPage&lt;/Nav.Link&gt;
                &lt;Nav.Link href=&quot;./func&quot;&gt;Func&lt;/Nav.Link&gt;
            &lt;/Nav&gt;
            &lt;/Container&gt;
            &lt;/Navbar&gt;
        &lt;/div&gt; 
    ) 
}
</code></pre>
<blockquote>
<p>Routes.jsx</p>
</blockquote>
<pre><code class="language-javascript">import { Routes, Route, Navigate } from &#39;react-router-dom&#39;  // 화면전환할때 쓰는 라우터 라이브러리
import Home from &#39;../views/Home.jsx&#39;;
import DetailPage from &#39;../views/Detail.jsx&#39;
import Cart from &#39;../views/Cart.jsx&#39;
import MyPage from &#39;../views/MyPage.jsx&#39;;
import Func from &#39;../views/Func.jsx&#39;;

const MyRoutes = ({shoes, setShoes}) =&gt; {
    return (
        &lt;Routes&gt;
            {/* &lt;Suspense fallback={ &lt;div&gt;로딩중임&lt;/div&gt; }&gt;&lt;/Suspense&gt; // 로딩중일시 보여줄 값 . 라우트를 감싸면 됨 */} 

            {/* 홈 이동 */}
            &lt;Route path=&quot;/&quot; element={ &lt;Navigate replace to=&quot;/home&quot;/&gt; } /&gt;  
            &lt;Route path=&quot;/home&quot; element={ &lt;Home shoes={shoes} setShoes={setShoes}/&gt; } /&gt;  

            {/* 상세 이동 - 전체 */}
            &lt;Route path=&quot;/detail&quot; element={ &lt;DetailPage shoes={shoes} /&gt; } /&gt;  

            {/* 상세 이동 - 각각 */}
            &lt;Route path=&quot;/detail/:id&quot; element={ &lt;DetailPage shoes={shoes} /&gt; } /&gt;  
            {/* shoes={shoes} : 자식 컴포넌트로 데이터 전달 */}
            {/* :id : url로 받은 데이터를 사용할 수 있게 해줌 */}

            {/* 카트 이동 */}
            &lt;Route path=&quot;/cart&quot; element={ &lt;Cart/&gt; } /&gt;

            {/* MyPage 이동 */}
            &lt;Route path=&quot;/myPage&quot; element={ &lt;MyPage/&gt; } /&gt;

            {/* Func 이동 */}
            &lt;Route path=&quot;/func&quot; element={ &lt;Func/&gt; } /&gt;

             {/* 404 이동 */}     
             &lt;Route path=&quot;*&quot; element={ &lt;div&gt;없는페이지누&lt;/div&gt; } /&gt;  

        &lt;/Routes&gt;
    )
}

export default MyRoutes</code></pre>
<p>라우터는 URL이 변경될 때마다 전체 페이지를 새로 고침하지 않고, 해당 URL에 맞는 컴포넌트를 동적으로 렌더링한다.</p>
<p>메인페이지, 상세페이지에선 데이터를 뿌려주기 위해서 shoes, setShoes 값들을 받아온다.</p>
<ul>
<li>동작순서
네비버튼 클릭 &gt; url 변경 &gt; 라우터가 url 감지 및 동작</li>
</ul>
<p>요로콤 간단하다. </p>
<hr>
<p>다음 게시글은 메인페이지인데 할게 많네
1,2 정도로 나눠야 할듯</p>
<p>contexp api
button
useState, props
context api 
react-query </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 구조]]></title>
            <link>https://velog.io/@dragon_jun/React-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@dragon_jun/React-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Mon, 24 Mar 2025 02:09:01 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-구조">프로젝트 구조</h2>
<p>├── public/          # 정적 파일들
│   └── assets/      # 이미지, 폰트 등
├── dist/             # 배포 코드
├── node_modules/            # 설치된 외부 패키지
├── src/             # 소스 코드
│   ├── components/  # 재사용 가능한 컴포넌트
│   ├── styles/      # 스타일 파일
│   ├── assets/      # 이미지, 폰트 등
│   ├── services/    # API 호출 및 비즈니스 로직
│   ├── utils/       # 유틸리티 함수
│   ├── hooks/       # 커스텀 후크 관
│   ├── views/               # 렌더링할 페이지나 템플릿리
│   ├── store/       # 상태 관리
│   ├── App.css/       # css
│   ├── main.jsx             # 애플리케이션의 진입점
│   └── App.jsx       # 루트 컴포넌트
├── index.html       # 메인 HTML 파일 (루트 디렉토리에 있어야 함)
├── .gitignore       # Git에 포함되지 말아야 할 파일
├── package.json     # 프로젝트 설정
├── package-lock.json        # 의존성 트리 고정 파일
├── vite.comfig.js     # vite 설정
├── eslint.comfig.js     # eslint 설정
└── README.md        # 프로젝트 설명서</p>
<h2 id="실행-흐름">실행 흐름</h2>
<p>index.html &gt; vite.config.js &gt; main.jsx &gt; App.jsx </p>
<hr>
<p>어우 복잡
이거 이곳저곳에서 Import 해서 쓰는 경우가 많아서 구조를 확실히 정리해서 어디서 가져올지 확실히 정의 해야겠다 생각함 
--</p>
<p>+++ 수정 예정임 많이 바귐</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 환경세팅]]></title>
            <link>https://velog.io/@dragon_jun/React-%ED%99%98%EA%B2%BD%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@dragon_jun/React-%ED%99%98%EA%B2%BD%EC%84%B8%ED%8C%85</guid>
            <pubDate>Mon, 24 Mar 2025 01:07:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-nodejs-다운로드">1. node.js 다운로드</h2>
<p>검색해서 받으셈</p>
<p>React.js : 프론트 UI 개발
node.js : db, 서버 연결</p>
<h2 id="2-작업폴더-만들기">2. 작업폴더 만들기</h2>
<p>폴더 마우스 우클릭 후 powershell 연다음에 
npm create vite@latest </p>
<p>이거하고 cmd가 하라는대로 이어서 하면 되는데 
아마 처음 세팅하면 온갖 경로오류, 보안오류 등등이 나옴</p>
<p>피곤함</p>
<h2 id="3-vscode-다운로드-후-실행">3. VSCode 다운로드 후 실행</h2>
<p>여기서 실행 </p>
<p>내가 오픈한 폴더에 따라 터미널 경로가 고정되기 때문에 너무 바깥쪽 폴더를 열면 터미널 열때마다 cd로 경로 바꿔야 되서 귀찮음</p>
<p>vscode는 이것저것 신기하고 편리한 확장 프로그램이 엄청 많음. 나중에 필요한거 하나씩 추가해서 쓰면 좋을듯
난 IntelliCode 요게 젤 신기함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React]]></title>
            <link>https://velog.io/@dragon_jun/React</link>
            <guid>https://velog.io/@dragon_jun/React</guid>
            <pubDate>Mon, 24 Mar 2025 00:34:56 GMT</pubDate>
            <description><![CDATA[<p>백개발자라 깊이 팔건 아니지만 혹시 나중에 만질일이 있을지도 모르고 기본은 알아야 같이 작업할수 있을거라 생각해서 시작했는데 생각보다 재밌었다.
우선 작업한 결과물이 바로바로 나오는게 신기했고 VSCode라는 툴의 UI가 예쁘고 편해서 좋았다. </p>
<p>현재 애플코딩 강의 완독이 완료됬고 React 학습 한 내용을 정리하려 한다.
강의마다 정리하는것 보다 기능별로 뭉텅이로 정리할 예정</p>
<p>막 만들긴 했는데 이거 시간내서 다시 정래해야 할듯
처음 만들면서 정리했는데 이제와선 많이 달라져서 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 깃허브 배포]]></title>
            <link>https://velog.io/@dragon_jun/React-14</link>
            <guid>https://velog.io/@dragon_jun/React-14</guid>
            <pubDate>Mon, 03 Mar 2025 09:54:48 GMT</pubDate>
            <description><![CDATA[<p>브라우저는 HTML CSS JS 이 세개의 언어만 해석가능 
그러므로 빌드해서 요 언어로 바꾼 다음에 배포해야함</p>
<ol>
<li><p>cmd창에 npm run build </p>
</li>
<li><p>우측상단 new repository 에 들어간 후 
 Repository name에 깃허브아이디.github.io </p>
</li>
<li><p>dist에 있는 파일 올리기</p>
</li>
</ol>
<p>음 쌤이 알려준 방법은 배포파일을 직접 올리는 형태이고 이러면 바로바로 수정한걸 배포하기 어렵다. 
깃의 기능을 사용하지 않는 그저 사이트만 띄우기 위한 방법인듯 싶음
보통 깃에는 코드를 올리고 브랜치로 배포를 따로 파는 방법을 씀
수정사항 있을때마다 Repository 만들건 아니잖아</p>
<h2 id="깃연결-및-배포">깃연결 및 배포</h2>
<ol>
<li><p>프로젝트에 깃 연결
sh에 git init &gt; git remote add origin <a href="https://github.com/%EC%82%AC%EC%9A%A9%EC%9E%90%EC%9D%B4%EB%A6%84/%EC%A0%80%EC%9E%A5%EC%86%8C%EC%9D%B4%EB%A6%84.git">https://github.com/사용자이름/저장소이름.git</a></p>
<blockquote>
<p>git add . //내 프로젝트 전부 깃에 올림 &gt; git commit -m &quot;첫 커밋&quot; &gt; git push -u origin main</p>
</blockquote>
</li>
<li><p>배포 branch 파기
npm install --save-dev gh-pages //branch 패키지 설치&gt; 깃허브에 Settings &gt; Pages &gt; Build and deployment &gt; Branch &gt; branch를 gh-pages로 저장</p>
</li>
<li><p>package.json에 스크립트 추가</p>
<pre><code class="language-json">&quot;scripts&quot;: {
&quot;predeploy&quot;: &quot;npm run build&quot;,    //npm run deploy 하기전에 dist로 빌드 먼저 자동으로 해줌
&quot;deploy&quot;: &quot;gh-pages -d dist&quot;    //branch에 배포파일 보내겠다.
}</code></pre>
</li>
<li><p>배포
npm run deploy</p>
</li>
</ol>
<hr>
<p>생각보다 깃에 반영되는데 시간걸림 
하다보면 병합,충돌 문제 발생할수 있음. 알아서 하시고</p>
<p>그 이후부턴
저장 &gt; git add . &gt; git commit -m &quot;수정comment&quot; &gt; git push -u origin main &gt; npm run deploy
순서대로 하면 됨</p>
<p>아니면 진짜 극도의 귀차니즘을 갖고있다면 package.json에</p>
<pre><code class="language-json">&quot;predeploy&quot;: &quot;git add . &amp;&amp; git commit -m &#39;Deploy updated site&#39; &amp;&amp; git push origin main&quot;,  
&quot;deploy&quot;: &quot;npm run build &amp;&amp; gh-pages -d dist&quot; </code></pre>
<p>이런식으로 할수도 있다. npm run deploy만 누르면 전부 자동으로 되서 진짜 편하다
내가 한것만 배포하고 싶으면 이렇게 하면 안되겠지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[넥사크로 13 - 채번]]></title>
            <link>https://velog.io/@dragon_jun/%EB%84%A5%EC%82%AC%ED%81%AC%EB%A1%9C-13-%EC%B1%84%EB%B2%88</link>
            <guid>https://velog.io/@dragon_jun/%EB%84%A5%EC%82%AC%ED%81%AC%EB%A1%9C-13-%EC%B1%84%EB%B2%88</guid>
            <pubDate>Thu, 27 Feb 2025 08:02:06 GMT</pubDate>
            <description><![CDATA[<p>ERP에서 값을 저장할 때, 데이터를 등록할 때마다 관리번호를 채번. 
예를 들어, &quot;20250227-0001&quot;과 같은 형식으로 관리번호를 생성해야 함</p>
<h2 id="프로그램-흐름">프로그램 흐름</h2>
<p>화면에서 프로그램ID와 기준일을 파라미터로 넘기면서 공통함수(this.gfn_GetTbnmNo)  호출
-&gt; 정의된 공통함수에서 받아온 파라미터를 InData에, 자바 경로를 strSvcUrl에 넣고 넥사 트랜잭션 호출 
-&gt; 트랜잭션을 통해 자바에 controller/service/implement  호출
-&gt; implement에 있는 insert 쿼리로 채번값을 채번관리 테이블에 넣고 select 쿼리로 채번 return</p>
<h2 id="쿼리">쿼리</h2>
<p>insert 쿼리는 MERGE..USING..WHEN MATCHED문과 CASE문을 썻다.
MERGE문은 문제있을때 처리할려고 쓴거고 
CASE문은 프로그램 ID로 확인하여 채번을 어떤 방식으로 처리 할건지 확인한다. 
공통환경설정에는 프로그램마다 어떤 채번을 사용할지 정해서 저장 되어있다.</p>
<h2 id="자바">자바</h2>
<pre><code class="language-java">화면에서는 프로그램id, 기준일자를 공통함수로 넘김
공통함수에서는 넘겨받은 값과 자바경로, in 데이터셋명(gds_Input), out 데이터셋명을 지정해서 트랜잭션 호출

/// Controller.java
@Controller
public class Controller {

    @Resource(name = &quot;Service&quot;)
    private Service sService;    

    @RequestMapping(value = &quot;/sy/Numbering.do&quot;)
    public NexacroResult Numbering(PlatformData platformData) throws Exception {    
        NexacroResult result = new NexacroResult();
        return Service.processPgNumbering(platformData);
    }       
}

/// Service.java
public interface Service {
    NexacroResult processPgNumbering(PlatformData platformData) throws Exception;    
}

/// ServiceImpl.java
@Service(&quot;Service&quot;)
public class ServiceImpl implements Service {

    @Resource(name=&quot;cmmDao&quot;)
    private CmmDao cmmDao;        

    @Override
    public NexacroResult processPgNumbering(PlatformData platformData) throws Exception {
        NexacroResult result = new NexacroResult();

        try {
            DataSet numberingData = platformData.getDataSet(&quot;gds_Input&quot;); // 데이터셋 정보 읽기

            if (numberingData == null) {
                throw new Exception(&quot;입력 데이터셋이 없습니다.&quot;);
            }

            Map&lt;String, Object&gt; saveMap = new HashMap&lt;&gt;(); // 저장처리 Map
            DatasetUtil.convertDsToMap(numberingData, saveMap, 0); // 데이터셋을 Map으로 변경

            // 채번정보 저장
            cmmDao.insert(&quot;Dao.insertDao002&quot;, saveMap);

            // 채번정보 조회
            List&lt;Map&lt;String, Object&gt;&gt; pgNumberingList = cmmDao.selectList(&quot;Dao.selectDao001&quot;, saveMap);

            if (pgNumberingList.isEmpty()) {
                throw new Exception(&quot;채번정보 조회 결과가 없습니다.&quot;);
            }

            result.addDataSet(&quot;gds_PgNumbering&quot;, pgNumberingList);
        } catch (Exception e) {
            // 예외 발생 시 로그 출력 및 예외를 다시 던짐
            e.printStackTrace();
            throw new Exception(&quot;Numbering 처리 중 오류가 발생했습니다.&quot;);
        }
        return result;
    }
}

/// CmmDao.java
@Repository(&quot;cmmDao&quot;)
public class CmmDao extends CmmAbstractMapper {

    @Resource(name = &quot;sqlSession&quot;)
    @Override
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }
}</code></pre>
<p>넥사크로 환경에서는 화면에서 자바로 값을 넘길 때 넥사크로 방식대로 처리하고, 자바에서는 넥사크로 함수 사용
리액트라면 화면에서 자바로 값을 넘길 때 Axios나 Fetch 쓰고, 자바에서는 넥사크로 함수 대신 setSqlSessionFactory 같은 방식으로 데이터를 처리</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[레거시 JAVA 스타일]]></title>
            <link>https://velog.io/@dragon_jun/%EB%A0%88%EA%B1%B0%EC%8B%9C-JAVA-%EC%8A%A4%ED%83%80%EC%9D%BC</link>
            <guid>https://velog.io/@dragon_jun/%EB%A0%88%EA%B1%B0%EC%8B%9C-JAVA-%EC%8A%A4%ED%83%80%EC%9D%BC</guid>
            <pubDate>Wed, 26 Feb 2025 07:29:50 GMT</pubDate>
            <description><![CDATA[<p>최신 스프링 특징</p>
<ol>
<li>객체생성을 @Autowired를 통해 자동 관리(의존성 주입)</li>
<li>ResponseEntity 사용으로 JSON 응답</li>
<li>@RequestBody 사용하여 JSON 데이터 자동 변환</li>
</ol>
<p>context.xml에 안쓰고 application.yml에 쓰겠지아마? </p>
<p>하지만 회사에서 스프링을 안쓰고 JSP/서블릿 타입의 레거시한 자바 스타일을 갖고 있을수 있다. </p>
<pre><code class="language-java">public class HrMakeFileAction extends AbstractAction {
    public void makeTaxFile(BusinessContext ctx) throws Exception {
        HrMakeFileInf hrMakeFileService = (HrMakeFileInf) ServiceManagerFactory.getService(&quot;HrMakeFileService&quot;);
        DataSet dsInput = (DataSet) ctx.getInputObject(&quot;ds_FileInput&quot;);

        // 서비스 호출하여 파일 생성 및 결과 받기
        DataSet dsResult = hrMakeFileService.makeTaxFile(dsInput);
        ctx.addOutput(&quot;ds_Result&quot;, dsResult);

        // dsResult에서 dsDetail20을 추출하여 사용할 수 있음
        DataSet dsDetail20 = dsResult.getDataSet(&quot;dsDetail20&quot;);
        ctx.addOutput(&quot;dsDetail20&quot;, dsDetail20);
    }
}
</code></pre>
<p>이런식으로 컨트롤 만들어놓고 구현체를 만듬</p>
<pre><code class="language-java">...
DataSet dsDetail20 = new DataSet();
...
 con = getConnection();
                SqlExecutor db = SqlMapFactoryHelper.makeSqlExecutor(con,SqlMapFactoryHelper.NEXACRO17);      
                SqlRequest sql = SqlMapFactoryHelper.makeSqlRequest(strSqlId, SqlMapFactoryHelper.NEXACRO17);
                sql.addParamObject(&quot;ds_Input&quot;, dsInput);             
                dsDetail20 = (DataSet) db.query(sql).getResultObject();//조회프로시저 호출 메소드
...</code></pre>
<p>구현체에는 이런식으로 넥사 내장 함수에 도움을 받아 Db에 연결함 </p>
<p>넥사크로를 안쓴다면 JDBC에 DriverManager를 이용해 dB경로, ID, PW, 쿼리 등등을 넘겨서 받아야함 </p>
<pre><code class="language-java">  con = DriverManager.getConnection(&quot;jdbc:mysql://localhost:3306/your_database&quot;, &quot;username&quot;, &quot;password&quot;);

                // 쿼리 실행
                String sql = &quot;SELECT * FROM your_table WHERE condition = ?&quot;;
                pstmt = con.prepareStatement(sql);
                pstmt.setString(1, dsInput.getString(0, &quot;your_condition_value&quot;)); // dsInput에서 값을 가져와 쿼리 파라미터 설정

                // 실행 후 결과 받기
                rs = pstmt.executeQuery();

                // 결과를 DataSet에 담기
                while (rs.next()) {
                    String columnValue = rs.getString(&quot;column_name&quot;);
                    // DataSet에 행 추가
                    dsDetail20.addRow();
                    dsDetail20.set(0, &quot;column_name&quot;, columnValue);
                }</code></pre>
<p>직관적이지 않아서 너무 불편..
spring이 좋아</p>
]]></description>
        </item>
    </channel>
</rss>