728x90
반응형

 

JSX란 리액트에서 생김새를 정의할 때 사용하는 문법이다. Javascript이나 HTML같이 생겼다.

 

JSX에서 꼭 지켜줘야 하는 문법이 있는데, 바로 태그를 꼭 닫아주어야 한다는 것이다.

 

 

컴포넌트 안에 변수를 선언하거나 style을 json형태로 선언하고, { } 중괄호 안에 담아서 속성을 부여할 수도 있다.

import React from 'react';
import Hello from './Hello';

function App() {
  const name = 'react';
  const style = { 
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: 24, // 기본 단위 px
    padding: '1rem' // 다른 단위 사용 시 문자열로 설정
  }

  return (
    <>
      <Hello />
      <div style={style}>{name}</div>
    </>
  );
}

export default App;

 

 

참조

https://react.vlpt.us/basic/04-jsx.html

728x90
반응형

'Front-End > React.js' 카테고리의 다른 글

React.js | 입문 | 조건부 렌더링  (0) 2021.09.16
React.js | 입문 | props  (0) 2021.09.16
React.js | 입문 | 컴포넌트 만들기  (0) 2021.09.16
React.js | 입문 | React hook  (0) 2021.09.15
React.js | 입문 | 환경 구성  (0) 2021.09.15
728x90
반응형

1. 전에 만들어 두었던 폴더 중 src 폴더 밑에 Hello.js 파일을 만든다.

 

Hello.js

import React from 'react';

function Hello() {
  return <div>안녕하세요</div>
}

export default Hello;

 

2. 그 다음 App.js 에서 Hello 컴포넌트를 불러올 수 있다.

 

App.js

import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <div>
      <Hello />
    </div>
  );
}

export default App;

 

정말 기본적인 컴포넌트 사용방법이다.

728x90
반응형

'Front-End > React.js' 카테고리의 다른 글

React.js | 입문 | 조건부 렌더링  (0) 2021.09.16
React.js | 입문 | props  (0) 2021.09.16
React.js | 입문 | JSX  (0) 2021.09.16
React.js | 입문 | React hook  (0) 2021.09.15
React.js | 입문 | 환경 구성  (0) 2021.09.15
728x90
반응형

https://github.com/GyeongSooJung/next_apollo_blog

 

GitHub - GyeongSooJung/next_apollo_blog

Contribute to GyeongSooJung/next_apollo_blog development by creating an account on GitHub.

github.com

코드는 위 블로그에서 확인 가능

 

1. 설치 방법은 간단하다. node설치 완료 후 ( 게시글 확인 부탁드립니다. ) 아래 명령어를 실행하면 된다.

npx create-next-app nextapp

 

2. 그럼 아래와 같이 파일들이 생성된다.

 

 

Next.js 는 pages 하위에 파일을 생성하면 그 파일이 url주소가 된다는 장점이 있다.

ex) pages/view/Main.js를 만들면 http://www.gsblog.site/view/Main 이런식

 

나같은 경우는 최상위 폴더 밑에 src폴더를 만들고, 쓰이는 component들은 src하위에 만들어줬다.

 

src 하위에 만들어진 파일들은 webpack으로 감싸진다고 한다. 덕분에 개발이 수월해졌다.

 

next_apollo_blog

 

728x90
반응형
728x90
반응형

1. 등장 배경

    - 리액트 컴포넌트는 클래스형 컴포넌트와 함수형 컴포넌트로 나뉜다. 클래스형 컴포넌트가 가지는 단점인

       코드가 길고 복잡하며, Logic의 재사용이 어렵다는 점 때문에 사용하기 힘들다는 의견이 많았다.

    - 그 때 Hooks의 등장으로 인해 함수형 컴포넌트들이 클래스형 컴포넌트들의 작업을 할 수 있게 되었다.

 

 

2. 예제

import React, { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div style={{ textAlign: "center" }}>
      <div style={{ fontSize: "100px" }}>{number}</div>
      <button onClick={() => setNumber(number + 1)}>더하기</button>
      <button onClick={() => setNumber(number - 1)}>빼기</button>
    </div>
  );
};

export default App;

 

보통의 자주 쓰는 hook은 useState와 useEffect 인데, 

컴포넌트 내에 객체나 변수가 필요할 때 useState로 선언을 해주고, JQuery에서 Object를 썼던 방식과 같이 컴포넌트에서도 객체를 다룰 수 있다. ( json 형태도 가능 )

 

사용 방법도 정말 간단하며, JQuery에서 썼던 방식보다 객체를 다루기 쉬우며, 클래스형 컴포넌트처럼 코드가 많이 복잡하지도 않다. 

 

useEffect같은 경우는 useState로 선언한 변수들의 변함에 따른 Logic을 구성할 수 있다.

useEffect(()=> { //페이지가 바뀔 때마다 설정값 바꿔줌
      let emptyArray = [];
      let start = (currentPage-1) * postNumber;
      let end = ((currentPage-1) * postNumber) + postNumber;
      
      for(var i = start ; i < end; i ++) {
            if(!listArrayall.array[i]) { //더이상 값이 없을 때의 조건문
              emptyArray.push("")
            }
            else {
              emptyArray.push(listArrayall.array[i]);
            }
          }
          
      setListArraypost(emptyArray);
      
      let emptyJson = {};
      let j = 0;
      for (var i = start; i < end; i ++) {
        if(listArrayall.array[i] !== undefined) {
          emptyJson[j] = {indexid : j, rowsid : listArrayall.array[i]._id, checked : false}
        }
        j++;
      }
      j = 0;
      setCheckedArray(emptyJson);
  } ,[currentPage]);

 

해당 코드는 내가 Next.js로 개발하면서 썼던 구문인데,  간단하게 말하자면 두번째 인자값으로 [] 배열 안에 변경이 감지될 변수( currentPage )를 넣어주고, 첫번째 인자로 변경이 감지되었을 때 로직( arrow 함수 )이 실행되도록 구현하였다.

 

 

 

참조

https://codingbroker.tistory.com/23

 

728x90
반응형

'Front-End > React.js' 카테고리의 다른 글

React.js | 입문 | 조건부 렌더링  (0) 2021.09.16
React.js | 입문 | props  (0) 2021.09.16
React.js | 입문 | JSX  (0) 2021.09.16
React.js | 입문 | 컴포넌트 만들기  (0) 2021.09.16
React.js | 입문 | 환경 구성  (0) 2021.09.15
728x90
반응형

 

1. aws 서버에 인스턴스를 만들고 터미널창을 켜서 React를 설치한다. ( 폴더 이름은 원하는대로 가능하다. )

npx create-react-app gsreact

 

2. 그럼 이런식으로 폴더들이 생성되는것을 확인할 수 있다.

 

 

3. 여기서 바로 npm start를 해보면 3000번 포트에 리액트 화면이 나오는것을 확인할 수 있다.

 

정말 쉽다!

728x90
반응형

'Front-End > React.js' 카테고리의 다른 글

React.js | 입문 | 조건부 렌더링  (0) 2021.09.16
React.js | 입문 | props  (0) 2021.09.16
React.js | 입문 | JSX  (0) 2021.09.16
React.js | 입문 | 컴포넌트 만들기  (0) 2021.09.16
React.js | 입문 | React hook  (0) 2021.09.15
728x90
반응형

 

 

 

전체적인 화면 구성은 이렇다. 해당 상품을 클릭했을 때 modal창이 떠서 옵션, 수량을 선택할 수 있고 장바구니에 추가하면

 

 

이런식으로 쿠키에 담겨 오른쪽에 나타나게 되고, 우측 하단에 total 금액까지 나와 결제를 하면

 

 

KG이니시스와 연동이되어 결제가 가능하게끔 구현하였다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형
728x90
반응형

코드에 누출되어선 안되는 기밀정보를 보통 루트 폴더 밑에 .env파일을 만들어 관리한다.

git을 사용할 때도 .gitignore 파일에 env를 추가하여 중요한 정보는 git에 등록되지 않게끔 하는 방법도 있다.

 

1. 먼저 dotenv npm 모듈을 설치한다.

$ npm i dotenv

 

 

2. .env 파일을 루트 폴더 밑에 작성한다.

DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3

 

 

3. 그 다음 node.js 환경의 파일들에 아래와 같이 추가하면 사용할 수 있다.

// index.js

require("dotenv").config();

console.log("DB_HOST:", process.env.DB_HOST);
console.log("DB_USER:", process.env.DB_USER);
console.log("DB_PASS:", process.env.DB_PASS);

 

 

728x90
반응형
728x90
반응형

이미 지금까지 설명해온 글들만 이해가 됐다면,  postNumber(게시물의 개수)를 다루는 법은 정말 쉽다.

 

1. 먼저 클라이언트 쪽에서 보여지는 드롭박스를 만든다.

<select id = "selectpage" name="data-table-default_length" aria-controls="data-table-default" class="custom-select custom-select-sm width-80 mb-2">
	<option class ="postnumber" value="10">10</option>
	<option class ="postnumber" value="25">25</option>
	<option class ="postnumber" value="50">50</option>
	<option class ="postnumber" value="100">100</option>
</select>

 

 

2. 그 다음 클릭 했을 경우의 selected된 값을 가져오고 selectpage 함수에 넘긴다.

$('#selectpage').click(function() {
  selectpage(pagingObject,{
  	option : $('#selectpage option:selected').val()
  })
});

 

 

3. selectpage 함수를 실행하고 마지막에 pagereload 실행

	// 페이지 개수 지정
	function selectpage(Object, jsondata) {
        if(Object.postNum != jsondata.option) {
			Object.postNum = jsondata.option
			pagereload(Object);
		}
    }

 

 

 

 

 

1. 그다음 엑셀 파일 다운로드는 먼저 클라이언트 코드는 이렇다.

<input type="button" class="ml-2 mb-2 btn btn-default" value="Excel" onclick="exceldownload(pagingObject);"/>

 

 

2. exceldownload 함수

function _excelDown(fileName, sheetName, Object, thlist, tdkeylist){
    	
    	const excelarray = Object.array;
    	var thlist = thlist;
    	var tdkeylist = tdkeylist;
    	
    	
			var html = ''; html += '<html xmlns:x="urn:schemas-microsoft-com:office:excel">';
			html += ' <head>';
			html += ' <meta http-equiv="content-type" content="application/vnd.ms-excel; charset=UTF-8">';
			html += ' <xml>';
			html += ' <x:ExcelWorkbook>';
			html += ' <x:ExcelWorksheets>';
			html += ' <x:ExcelWorksheet>';
			html += ' <x:Name>' + sheetName + '</x:Name>';
			html += ' <x:WorksheetOptions><x:Panes></x:Panes></x:WorksheetOptions>';
			html += ' </x:ExcelWorksheet>';
			html += ' </x:ExcelWorksheets>';
			html += ' </x:ExcelWorkbook>';
			html += ' </xml>';
			html += ' </head>';
			html += ' <body>';
			// ----------------- 시트 내용 부분 -----------------
			
    			html += "<table >";
    			html +=		"	<thead>";
    			html +=		"		<tr>";
    			for ( var i = 0; i < thlist.length; i ++) {
    			    html += "<th>";
        			html += thlist[i];
        			html += "</th>";
    			}
    			
    			html += "</tr>";
    			html +=		"	</thead>";
    			html +=		"	<tbody>";
    			
    			
    			for (var i = 0; i < Object.array.length; i ++) {
    				html +=		"		<tr>";
    				for(var j = 0; j < tdkeylist.length; j ++) {
    				html +=		"		<td>";
    				if(tdkeylist[j] == "CA")
    				{
    					html +=	moment(Object.array[i][tdkeylist[j]]).format('YYYY-MM-DD');
    				}
    				else if(tdkeylist[j] == "PD") {
    					if (parseInt(Object.array[i].PD/60)==0)
							html += ((Object.array[i][tdkeylist[j]])%60)+"s";
						else 
							html += parseInt((Object.array[i][tdkeylist[j]])/60)+"m "+(parseInt(parseInt(Object.array[i][tdkeylist[j]])%60))+"s"
    				}
    				else 
    				{
    					html +=	Object.array[i][tdkeylist[j]];
    				}
    				html +=		"		</td>"   ;
    				}
    				html +=		"		</tr>";
    			}
    			html +=		"	</tbody>";
    			html +=		"</table>";
// 			}
			
			//시트 내용 부분 -----------------
			html += ' </body>'; html += '</html>';
			// 데이터 타입
			var data_type = 'data:application/vnd.ms-excel';
			var ua = window.navigator.userAgent; var blob = new Blob([html], {type: "application/csv;charset=utf-8;"});
			
			if ((ua.indexOf("MSIE ") > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) && window.navigator.msSaveBlob)
			{ // ie이고 msSaveBlob 기능을 지원하는 경우
				navigator.msSaveBlob(blob, fileName);
			}
			else { // ie가 아닌 경우 (바로 다운이 되지 않기 때문에 클릭 버튼을 만들어 클릭을 임의로 수행하도록 처리)
				var anchor = window.document.createElement('a');
				anchor.href = window.URL.createObjectURL(blob);
				anchor.download = fileName; document.body.appendChild(anchor); anchor.click();
				// 클릭(다운) 후 요소 제거
				document.body.removeChild(anchor);
			}
		}
		
function exceldownload(Object)
{ // 대상 테이블을 가져옴
    var table = document.getElementById("data-table-combine");
    if(table){ // CASE 대상 테이블이 존재하는 경우
    
    
        
        var thlist = []; // th 들어갈 배열
        var tdkeylist = []; // tr을 추출하기위해 배열의 식별 값을 가져옴
        
        for(var i = 0; i < $('#memDiv1 th').length; i ++) {
            if($('#memDiv1 th').eq(i).text() != "") {
                thlist.push($('#memDiv1 th').eq(i).text())
                tdkeylist.push($('#memDiv1 th').eq(i).attr('name'))
            }
        }
        
        // 엑셀다운 (엑셀파일명, 시트명, 내부데이터HTML)
        _excelDown("oasis_excel.xls", "Sheet", Object, thlist, tdkeylist)
    }
    
}

 

현재 보여지는 리스트들만 담아서 데이터를 엑셀로 다운로드 받을 수 있게끔 하였다.

728x90
반응형
728x90
반응형

1. 먼저 클라이언트 쪽에서 이전에 올렸던 carlist_condition 부분을 참고하면

insertTr += "<th width='28%' name='CN'>"+i18nconvert("CN")+"<a href='javascript:sortList(pagingObject,CN);'><i id = 'CN' class='float-right mx-1 fas fa-lg fa-fw m-t-3 ";
	if(Object.sort == "CN-2")
		insertTr += "fa-sort-up'></a></i></th>";
	else if(Object.sort == "CN")
		insertTr += "fa-sort-down'></a></i></th>";
	else 
		insertTr += "fa-sort'></a></i></th>";

 

 

 

이런식으로 sort 값에 따라 텍스트 옆에 화살표 모양이 바뀌는것을 설정해 둔 후 ( 정렬할 텍스트를 클릭할 경우 )

sort에 "-2" 텍스트를 추가하여 -2가 있을 경우 백엔드에서 거꾸로 sort 해주게끔 설계하였다.

 

 

2. sortList 함수 실행 후 pagereload 함수를 실행하여 백엔드에서 받아온 데이터를 리스트에 뿌려주게끔 하였다.

// 정렬 기능
function sortList(Object, sort) {
	var pagelist = Object.array;
	var sortPlus = sort.id;
	
	if(document.getElementById(sort.id).classList.contains('fa-sort-down')) {
 		var sortPlus2 = sortPlus + "-2";
	}
	else {
 		var sortPlus2 = sortPlus;
	}
	
	Object.array = pagelist;
	Object.sort = sortPlus2;
	pagereload(Object);
}

 

 

728x90
반응형
728x90
반응형

 

pagingObject에 있던 속성들 중

searchtext 검색 옵션(문자)
searchdate 검색 옵션(날짜)
search 검색 옵션

이 값들 위주로 검색을 구현한다.

 

 

1. 먼저 클라이언트 쪽에선 위에 화면구성에서 보여준 것과 같이 날짜에 관한 텍스트. 검색 옵션에 관한 드롭다운, 검색 내용이들어갈 input, 검색 버튼이 들어가게 구성한다.

 

<div class="search-box mt-2 mb-3 py-3 px-3 d-flex justify-content-between align-items-center bg-light">
  <div id ="searchdatetext" class ="d-flex justify-content-between align-items-center">
      <input type="date" id = "searchdatetext1" class ="form-control form-control-sm" placeholder ="" />
      &nbsp;&nbsp;~&nbsp;&nbsp;
      <input type="date" id = "searchdatetext2" class ="form-control form-control-sm" placeholder ="" />
  </div>
    <div class="d-flex justify-content-between align-items-center">
      <div class ="mr-2">
        <div class="btn-group m-r-5">
          <a id="searchoption" href="javascript:;" class="btn btn-sm btn-white">{{__('search')}}</a>
          <a href="#" data-toggle="dropdown" class="btn btn-sm btn-white dropdown-toggle" aria-expanded="false"><b class="caret"></b></a>
        <div class="dropdown-menu dropdown-menu-right">
          <a href="javascript:searchoption('ANA',pagingObject,{ANA : 'ANA',CN : 'CN',CPN : 'CPN'});" class="dropdown-item">{{__('agent_branch')}}</a>
          <a href="javascript:searchoption('CN',pagingObject,{ANA : 'ANA',CN : 'CN',CPN : 'CPN'});" class="dropdown-item">{{__('car_number')}}</a>
          <a href="javascript:searchoption('CPN',pagingObject,{ANA : 'ANA',CN : 'CN',CPN : 'CPN'});" class="dropdown-item">{{__('car_customer_phone')}}</a>
        </div>
      </div>
    </div>

    <div class="mr-2">
    	<input  id ="searchtext" type="text" class="w-100 form-control form-control-sm" placeholder ="" />
    </div>
    <div>
    	<button id ="searchbutton" class="btn btn-sm btn-primary text-white" onclick="searchtext(pagingObject)"><i class="mx-1 fas fa-lg fa-fw fa-search align-items-center"></i></button>
    </div>
    <a href='javascript:;' id ="gotolist" onclick=gotolist(pagingObject) class='d-none ml-1 btn btn-sm btn-primary mr-1 px-2'>{{__('all')}}</a>
  </div>
</div>

 

 

2. 그 다음 searchtext를 아이디로 갖는 부분에 keyup함수를 써서 key.keyCode가 13일 경우( 엔터를 눌렀을 경우 ) 검색이 이루어지게끔 한다. ( 버튼을 눌렀을 경우와 동일 )

	$('#searchtext').keyup(function(key) {
			if(key.keyCode == 13) {
				searchtext(pagingObject)
			}
		});

 

 

3. 검색 옵션을 지정했을 경우에 Object의 search 옵션을 바꿔주고, 버튼을 누르거나 엔터 키를 입력했을 경우에는

입력 했던 곳들의 value를 가져와서 pagereload를 실행하고, 백엔드에서 옵션별로 데이터를 다시 가져오게 된다.

	//검색 옵션 지정
	function searchoption(opt,Object,jsondata) {
    	Object.search = opt;
    	$("#searchoption").empty();
    	
    	var string = "";
    	
    	for (var item in jsondata) {
    		if (opt === item) {
    			string = i18nconvert(item);
    		}
    	}
    	$("#searchtext").val('');
    	$("#searchoption").append(string);
    	$("#searchoption").show();
    }
    
    //검색기능
    function searchtext (Object) {
		if(Object.search == "" && $('#searchtext').val() != "") {
			alert(i18nconvert('search_option_error'));
		}
		else{
	    	if(($('#searchdatetext1').val() != "") && ($('#searchdatetext2').val() != "")) {
				Object.searchtext = $('#searchtext').val();
				Object.searchdate ="";
	    		Object.searchdate += $('#searchdatetext1').val();
	    		Object.searchdate += "~";
	    		Object.searchdate += $('#searchdatetext2').val();
				pagereload(Object);
				document.getElementById('gotolist').classList.remove('d-none');
			}
			else {
				Object.searchtext = $('#searchtext').val();
				pagereload(Object);
				document.getElementById('gotolist').classList.remove('d-none');
			}
		}
	}

 

 

※ 이전에 게시했던 car list 백엔드 라우터 ( searchCNU는 미들웨어에서 이 사이트에서 필요한 내용을 실행한 것이므로 무시하고 원하는 데이터를 얻을 수 있게끔 하시면 됩니다. )

//car list
router.post('/car_list', isNotLoggedIn, DataSet, agentDevide, async function(req, res, next) {
  const CNU = req.body.CNU;
  var sort = req.body.sort;
  var search = req.body.search;
  var searchtext = req.body.searchtext;
  var searchdate = req.body.searchdate;
  var sortText = "";
  var sortNum = 0;
  var cars = new Object;
  
  // company list에서 접속한 것인지 확인
  if(CNU.includes("#") == true) {
    req.searchCNU = CNU.split("#")[0]; // '#' 을 잘라
  }
  else {
    req.searchCNU = req.searchCNU; // 기존 middleware에서 받아온 본사,지점 CNU 그대로 다시 담음
  }
  
  // 정렬 기능
  if(sort.includes('-') == true) {
    sortText = sort.split('-')[0];
    sortNum = 1;
  }
  else {
    sortText = sort;
    sortNum = -1;
  }
  
  try {
    
    var doc = {
      lookup : { from : "Company", localField : "CNU", foreignField : "CNU", as : "ANA" } ,
      unwind : "$ANA",
      match : {},
      project : { CN : '$CN', CPN : '$CPN', CA : '$CA', ANA : '$ANA.ANA' },
      sort : { [sortText]: sortNum }
    }
    
    if(cars.length == 0) {
        return res.send({ result : "nothing" });
      }
    else {
      if (searchdate) {
      var searchtext2 = searchdate.split("~");
        if(search == "ANA") {
          doc.match = { "CNU": { $regex: req.searchCNU }, "ANA.ANA" : {$regex:searchtext}, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } };
        }
        else if(search == "CN") {
          doc.match = { "CNU": { $regex: req.searchCNU }, "CN" : {$regex:searchtext}, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } };
        }
        else if(search == "CPN") {
          doc.match = { "CNU": { $regex: req.searchCNU }, "CPN" : {$regex:searchtext}, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } };
        }
        else {
          doc.match = { "CNU": { $regex: req.searchCNU }, "CA" : { $gte: new Date(searchtext2[0]+"T00:00:00.000Z"), $lt: new Date(searchtext2[1]+"T23:59:59.999Z") } } ;
        }
      }
      else {
          if(search == "ANA") {
            doc.match = { "CNU": { $regex: req.searchCNU }, "ANA.ANA" : {$regex:searchtext} };
          }
          else if (search =="CN") {
            doc.match = { "CNU": { $regex: req.searchCNU }, "CN" : {$regex:searchtext} };
          }
          else if (search =="CPN") {
            doc.match = { "CNU": { $regex: req.searchCNU }, "CPN" : {$regex:searchtext} };
          }
          else {
            doc.match ={ "CNU": { $regex: req.searchCNU } };
          }
      }
    }
  
    cars = await modelQuery(QUERY.Aggregate,COLLECTION_NAME.Car,doc,{});
    if(cars.length == 0) {
      return res.send({ result : "nothing" });
    }
    
    var carlist = [];
    if(cars.length) {
      for(var i = 0; i < cars.length; i ++) {
        carlist[i] = cars[i];
      }
    }
    
    res.send({ result: true, pagelist : carlist });
  
  } catch(err) {
    console.error(err);
    next(err);
  }
});
728x90
반응형

+ Recent posts