import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import "./Login.scss";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
pw: "",
};
}
hadleValueID = (e) => {
this.setState({
email: e.target.value,
});
};
hadleValuePW = (e) => {
this.setState({
pw: e.target.value,
});
};
// 로그인용 버튼
hadleBtn = (e) => {
fetch("http://10.58.5.19:8000/user/sign-in", {
method: "POST",
body: JSON.stringify({
email: this.state.email,
password: this.state.pw,
}),
})
.then((res) => res.json())
.then((res) => {
if (res.token) {
localStorage.setItem("aesopToken", res.token);
//로그인시 nav에 email 전달하는 함수 (부모에게 전달)
this.props.showID(this.state.email);
this.props.handleLogin();
}
});
};
// 회원가입용
handleSignUp = (e) => {
this.props.history.push("/signup");
};
render() {
return (
<div className="Login">
<div className="modalBody">
<div className="introduceBox">
<h2 className="loginTitle">안녕하세요.</h2>
<div className="loginSub">유효한 이메일 주소를 입력하세요</div>
</div>
<div className="formText">
<label>
<input
onChange={this.hadleValueID}
className="formTextInput"
name="email"
placeholder="이메일 주소"
/>
{/* <span className="FormTextLabel">이메일 주소</span> 이부분은 애니메이션이 요구됨. 해결방안이 시급. */}
</label>
</div>
<div className="formText">
<input
onChange={this.hadleValuePW}
className="formTextInput"
type="Password"
placeholder="비밀번호"
/>
</div>
<button
onClick={this.hadleBtn}
type="button"
className={
this.state.email.length > 5 && this.state.pw.length > 5
? "activeBTN"
: "unactiveBTN"
}
>
<div className="btnContent">로그인</div>
</button>
<button
onClick={this.handleSignUp}
type="button"
className="signUpcolor"
>
<div className="signUpBtn">회원가입</div>
</button>
</div>
</div>
);
}
}
export default withRouter(Login);
리팩토링이 진행된 코드이다.
내가 했어야하는데, 내 팀원이 modal창을 만들면서 해버렸다.
난 상세페이지 구현이 예상외로 느려서 팀원에게 민폐를 끼쳤다.....
나도 몰랐던 오류가 발견되었는데, 철자오류 발견.
Sign을 쳐야할 것이 sing으로 classname이 적혀있던 게 보였다.
멘토님이 지적하길래 그제야 발견.
아무튼 수정해서 정상적으로 작동이 되었다.
그 다음으로 구현한 Sign up(회원가입 페이지)
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import "./Signup.scss";
class Signup extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
pw: "",
pwconfirm: "",
firstname: "",
lastname: "",
pwError: false,
};
}
hadleValueEmail = (e) => {
this.setState({
email: e.target.value,
});
};
hadleValuePW = (e) => {
this.setState({
pw: e.target.value,
});
};
hadleValuepwConFirm = (e) => {
this.setState({
pwconfirm: e.target.value,
});
if (e.target.value.length > 0) {
if (e.target.value === this.state.pw) {
this.setState({ pwError: false });
} else {
this.setState({ pwError: true });
}
}
};
hadleValueFirstName = (e) => {
this.setState({
firstname: e.target.value,
});
};
hadleValueLastName = (e) => {
this.setState({
lastname: e.target.value,
});
};
handlesignUP = (e) => {
fetch("http://10.58.1.133:8000/user/sign-up", {
method: "POST",
body: JSON.stringify({
email: this.state.email,
password: this.state.pw,
first_name: this.state.firstname,
last_name: this.state.lastname,
}),
})
.then(res => res.json())
.then(res => {
console.log(res)
if (res.token) {
localStorage.setItem("aesop", res.token);
alert("회원가입을 환영합니다.");
this.props.history.push("/");
} else {
alert("이메일과 비밀번호를 확인해주세요.");
}
});
};
handleHaveAccount = (e) => {
this.props.history.push("/Login");
};
render() {
return (
<div className="Signup">
<form className="loginForm">
<div className="modalheadingWrap">
<h2 className="modalTitle">
처음 만나 뵙게 되네요. 이솝에 오신 것을 환영합니다.
</h2>
<div className="modalSubTitle">
계정을 만들려면 아래에 세부 정보를 입력하십시오.
</div>
</div>
<div className="formRow">
<div className="formText">
<label>
<input
onChange={this.hadleValueEmail}
className="formTextInput"
name="email"
placeholder="이메일 주소"
type="email"
/>
{/* <span className="FormTextlabel">이메일 주소</span> */}
</label>
</div>
</div>
<div>
<div className="formRow">
<div className="formText">
<label>
<input
onChange={this.hadleValuePW}
className="formTextInput"
name="password"
placeholder="패스워드"
type="password"
/>
{/* <span className="FormText-label">패스워드</span> */}
</label>
</div>
</div>
<div className="formRow">
<div className="formText">
<label>
<input
onChange={this.hadleValuepwConFirm}
className="formTextInput"
name="passwordConfirm"
placeholder="패스워드 확인"
type="password"
/>
<div
className={
this.state.pwError
? "errorMessage"
: "noneErrorTextMessage"
}
>
이전에 사용했던 패스워드를 입력하세요.
</div>
{/* <span className="FormText-label">패스워드 확인</span> */}
</label>
</div>
</div>
<div className="formRowbox">
<div className="formTextLoginName">
<label>
<input
onChange={this.hadleValueFirstName}
className="formTextInput"
maxLength="100"
name="firstName"
placeholder="성"
type="text"
/>
{/* <span className="FormText-label">성</span> */}
</label>
</div>
<div className="formTextLoginName">
<label>
<input
onChange={this.hadleValueLastName}
className="formTextInput"
maxLength="100"
name="LastName"
placeholder="이름"
type="text"
/>
{/* <span className="FormText-label">이름</span> */}
</label>
</div>
</div>
</div>
<button
onClick={this.handlesignUP}
className={
this.state.email.length > 5 &&
this.state.pw === this.state.pwconfirm &&
this.state.firstname.length >= 1 &&
this.state.lastname.length >= 1
? "btnSignUPActive"
: "btnSignUPunActive"
}
type="submit"
>
<div className="btnContent ">
<span className="btnLabel">등록</span>
</div>
</button>
<div className="loginHaveAccount">
<button
onClick={this.handleHaveAccount}
className="loginHaveAccountBTN"
>
이솝 계정을 가지고 계십니까?
</button>
</div>
</form>
</div>
);
}
}
export default withRouter(Signup);
이것도 원래는 리팩토링이 진행되지 않았는데, 팀원이 내가 바쁘다보니 그냥 해버렸다.
그런데, 회원가입창에서 메인페이지로 넘어가지 않는다고해서 무슨 문제인가 찾아보다가 버튼 이벤트로 인해서 화면이 refresh 되는 것을 막기위해
e.preventDefalut()(해당이벤트를 방지하는 것) 해당 버튼핸들러에 먹이고 찾아보았다.
원인은 백엔드쪽에서 토큰값을 주지 않았던 것.
이게 문제가 초기 레이아웃 완성했을때 백엔드쪽에서 토큰값을 제공했는데, 막상 api와 붙여보니 해당 토큰이 들어오지 않아서 메인창으로 넘어가지 않았던 것이다.
console.log 덕분에 찾았다.
귀찮아도 찍어요! console.log!!! ( ッ◕ ܫ◕)ッ'
그리고 다음으로 내가 받은 상세 페이지.
진짜로 구조가 어려웠다.
해당 페이지를 구현하기 위해서 처음에는 한페이지로 모든 컴포넌트를 구현하려고 했지만, 결국 하나의 상품을 호버하기위해서 컴포넌트를 모두 나눠버려야만 했다.
여기서 날린 시간이 대략 5~6시간..
이 한페이지를 구현하기위해서 내가 만든 컴포넌트만 5개였고, 심지어 footer, nav 목록 상단은 다른 팀원이 만든 것을 가져온 것이다.
여러인원의 협업으로 진행되었음을 알린다.
import React, { Component } from "react";
import Nav from "../../Components/Nav/Nav";
import Productfilternav from "../../Components/Productfilternav";
import Mapcomponent from "./Component/Mapcomponent";
import Footer from "../../Components/Footer/Footer";
import { withRouter } from "react-router-dom";
import "./Maplist.scss";
class MapList extends Component {
constructor(props) {
super(props);
this.state= {
goods:[]
};
}
componentDidMount() {
fetch(`http://15.164.220.49:8080/board/1/${this.props.match.params.id}`)
.then(res => res.json())
.then(res => {
this.setState({ goods: res.data.items})
})
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.match.params.id !== this.props.match.params.id) {
console.log(this.props)
fetch(`http://15.164.220.49:8080/board/1/${this.props.match.params.id}`)
.then(res => res.json())
.then(res => {this.setState({ goods: res.data.items})
})
}}
render() {
return(
<div className="Productcolumn">
{this.state.goods.map(product => {
return (
<>
<Nav />
<Productfilternav />
{this.state.goods.length> 0 && <Mapcomponent goods={product} />}
<Footer />
</>
);
})}
</div>
);
}
}
export default withRouter(MapList);
최상단의 컴포넌트.
렌더하는 부분에서 return의 아래를 보면 map함수가 돌아가고 있다.
이부분에서 state의 길이 확인하는 부분이 있는데 넣은 이유는 초기값이 []빈배열이라 불러올때 오류가 발생하기 때문이다.
그렇기에 해당 오류를 잡기위해 조건문을 집어넣었고, 불러올때 발생하는 오류를 잡아내었다.
render() {
return(
<div className="Productcolumn">
{this.state.goods.map(product => {
return (
<>
<Nav />
<Productfilternav />
{this.state.goods.length> 0 && <Mapcomponent goods={product} />}
<Footer />
</>
);
})}
</div>
);
}
그 다음으로 props 값을 받는 mapcomponent이다.
import React, { Component } from "react";
import { Link } from 'react-router-dom';
import Mapcategoryinfo from "./Mapcategoryinfo";
import Mapgoods from "./Mapgoods";
import Underbar from "./Underbar";
import "./Mapcomponent.scss";
class Mapcomponent extends Component {
constructor(props) {
super(props);
}
render() {
const goods = this.props.goods;
const categoryInfo= goods.category_info;
const sales = goods.products;
const nextpage = goods.next_category;
return(
<>
<section className="Mapcomponent">
<Mapcategoryinfo categoryInfo={categoryInfo} />
<Mapgoods sales={sales} />
</section>
<Underbar nextpage={nextpage} />
</>
)
}
}
export default Mapcomponent;
여기서 원래는 이름을 통일해서 지었다가, 다른 팀원과 이름이 비슷해서 그냥 내가 바꿔버렸다.
여기 부분에서 각자 다른 종류의 데이터가 들어갈 수 있도록 정리하였고, 이를 진행할 수 있게하였다.
여기도 특이한 부분이 있는데,
return(
<>
<section className="Mapcomponent">
<Mapcategoryinfo categoryInfo={categoryInfo} />
<Mapgoods sales={sales} />
</section>
<Underbar nextpage={nextpage} />
</>
)
섹션으로 묶은 부분이 있다. 이 부분을 통해서 해당 객체가 다름에도 불구하고 옆으로 줄지어 나란히 설수있는
효과를 적용할 수 있게 되었다.
원래는 아래와 같이 적용했는데,
return(
<>
<Mapcategoryinfo categoryInfo={categoryInfo} />
<Mapgoods sales={sales} />
<Underbar nextpage={nextpage} />
</>
)
해당 칸을 아예 할당되는 구조가 되는지라 이를 해결하기위해서 해당 컴포넌트를 묶어버리고 display: flex를 먹여주는 방법으로 123이렇게 나열되는 방식을 구현할 수 있었다.
이제 카테고리 info 자료를 보자.
import React, { Component } from "react";
import "./Mapcategoryinfo.scss";
class Mapcategoryinfo extends Component {
constructor() {
super();
}
render() {
const category= this.props.categoryInfo;
return(
<>
<div className="Mapcategoryinfo">
<div className="textTitleBOX">
<div className="introTitle">
<div className="introDescription">
<h2 className="introDescriptionHeadline" >
{category.description_title}
</h2>
<p className="introDescriptionINFO">
{category.description_content}
</p>
</div>
</div>
</div>
</div>
</>
);
}
}
export default Mapcategoryinfo;
처음에 자료를 어떻게 뽑아야하는가 했는데, 해당 백엔드 데이터가 {} 객체여서 일부러 타이틀로 간단히 넣어버릴 수 있었다.
굉장히 쉬웠던 부분이었다.
다음은 mapgoods
import React, { Component } from "react";
import Mapgood from "./Mapgood";
import "./Mapgoods.scss";
class Mapgoods extends Component {
constructor() {
super();
}
render() {
const sales =this.props.sales;
return (
<>
{sales.map(product => {
return (
<Mapgood product={product} />
);
})
}
</>
)}
}
export default Mapgoods;
배열로 돌린 것의 집합을 잘라서 1개씩 넣어주기 위해서 만든 컴포넌트였다.
막상 만들고보니 이전에서 잘라 넣어줬다면 해결되었을 쓸모 없는(?) 컴포넌트 같은데,
실력이 더 좋았으면 해당 컴포넌트에 그냥 자연스럽게 1개의 상품을 구현할 수 있었지 않을까 싶다.
이제 해당 상품의 마지막인 mapgood이다. 그냥 만들다보니 mapgoods는 상품이지만 mapgood은 그냥 '맵이 좋다'라는것을 다 만들고보니 깨달았다...
s를 빼면 그냥 단수가 된다. 이런 생각으로 만든거 같은데, 당시에 급하다보니 아무런 생각없이 만든 네이밍 같다.
import React, { Component } from "react";
import { Link, withRouter } from 'react-router-dom';
import "./Mapgood.scss";
class Mapgood extends Component {
constructor() {
super();
this.state = {
btnChange: true,
display: false,
}
}
hadleChangeBtn = (e) => {
this.setState ({
btnChange : false
});
setTimeout(() => {
this.setState({
btnChange:true
})
}, 700);
}
hoverOverChange = (e) => {
this.setState({
display: true
})
}
hoverOutChange = (e) => {
this.setState({
display : false
})
}
render() {
const product = this.props.product;
return(
<div onMouseOver={this.hoverOverChange}
onMouseOut={this.hoverOutChange}
className={this.state.display ?
`hoverState subcategoryProducts `: `nonehoverState subcategoryProducts`}>
<div className="subcatProductWrapper">
<div className="pictureImgLinkControl">
<Link className="pictureImgLinkTag" to="">
{/* <Link className="pictureImgLinkTag" to={product.history.push(`/Maplist/${Mapgood.id}`)}> */}
<div className="productPicture">
<img className="pictureImg" src={product.image_url} alt=""></img>
</div>
</Link>
</div>
<div className="productSizePrice">
<Link className="textdeco" to="" >
<h5 className="productSizePriceName">{product.name}</h5>
<div className="productSizePriceInfo">
<span>{product.size[0].size}</span>
<span className="spanSlash">/</span>
<span>{product.size[0].price}</span>
</div>
</Link>
</div>
<div className="productDetailLink">
<Link to="">
<div className="productDetails">
<ul className="productList">
<li className="productDetailsListItem">
<div className="productDetailsTitle">피부 타입</div>
<div className="productDetailsContent">
{product.skin_types.map((el) => {
const num = product.skin_types.length - 1;
const lastEl = product.skin_types[num];
if (el === lastEl) {
return `${el.name}`;
} else {
return `${el.name}, `;
}
})}
</div>
</li>
<li className="productDetailsListItem">
<div className="productDetailsTitle">
사용감
</div>
<div className="productDetailsContent">
{product.usability}
</div>
</li>
</ul>
</div>
<div className={this.state.display ?
`hoverState productWrapper` : `nonehoverState productWrapper`} >
<button className="productCartBtn" type="button">
<div className="btnContent">
<div onClick={this.hadleChangeBtn}
className={this.state.btnChange ? "btnLabel" : "noneBtnLabel"}>
카트에 추가 — {product.size[0].price}
</div>
<span className={this.state.btnChange ?
"btnLabelAction" : "nonebtnLabelAction" }>
카트에 추가됨
</span>
</div>
</button>
</div>
</Link>
</div>
</div>
</div>
)
}
}
export default Mapgood;
비구조화도 진행했으면 더 깔끔했겠지만 진행하지 못한게 아쉬웠다.
이걸하면서 다시금 느꼈는데, 배열의 []접근방법을 잘 몰라서 카트에 추가되는 부분을 일부러 맵을 돌려 뽑았다가
인덱스 [0]을 주어 해당값을 수월하게 뽑았다.
{product.skin_types.map((el) => {
const num = product.skin_types.length - 1;
const lastEl = product.skin_types[num];
if (el === lastEl) {
return `${el.name}`;
} else {
return `${el.name}, `;
}
})}
해당 코드의 제일 인상깊은 코드였다.
이 코드가 왜 어려웠냐면, 배열의 길이가 적으면 쉼표를 포함해서 출력하고, 배열의 길이와 같다면 그냥 인자만 출력하는 부분이었다.
이렇게 백틱(`)으로 쉼표를 포함하여 출력하는 부분을 팀장(최운정 PM)이 해줬는데 내 실력으로는 생각도 못한 부분이라 아주 인상깊었다.
마지막으로 내가 만든 부분의 Underbar이다.
해당 컴포넌트는 종류가 여러가지가 아니었고, 해당 컴포넌트를 백엔드에서 손쉽게 그냥 다음 페이지 자료를 보내줬기에 아주 손쉽게 해결하였다.
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import "./Underbar.scss";
class Underbar extends Component {
constructor() {
super();
this.state = {
products : []
}
}
clickHandle = (id) => {
this.props.history.push(`${this.props.nextpage.id}`)
}
render() {
const nextpage = this.props.nextpage;
return(
<>
<div className="nextpageDark">
<div className="nextpageWrapper">
<div className="nextpageImgWrapper">
<div className="nextpageImgBOX">
<img className="nextpageImgSize"
src={nextepage.image_url} />
</div>
</div>
<div className="nextpageInfo">
<h5 className="nextpageInfoTitle">다음 단계</h5>
<h2 className="nextpageInfoContent">{nextpage.description_title}</h2>
<div className="nextpageBtnWrapper">
<button className="nextpageBtn" type="button" onClick={this.clickHandle}>
<div className="btnContent">
<span className="btnLabel">{nextpage.name}</span>
<svg className="btnIconBector" role="img" viewBox="0 0 50 50">
<g>
<path d="M30.1,5.3L50,25.1L30.1,45h-6.6l18-17.6H0v-4.8h41.5l-18-17.6h6.6V5.3z"></path>
</g>
</svg>
</div>
</button>
</div>
</div>
</div>
</div>
</>
)
}
}
export default withRouter(Underbar);
여기서 만든 레이아웃은 그리어렵지 않았다. 제일 어려웠던 부분은 오히려 여기서 다음 페이지로 넘어가는 부분.
clickHandle = (id) => {
this.props.history.push(`${this.props.nextpage.id}`)
}
이 부분이 어려웠다. 무엇을 어떻게 던져 넘기는 방법을 모르다보니 해결이 어려웠었다.
해당 기능을 잘 쓸수 있다? 이건 아직아닌 것 같아서 아쉽다... (:ᘌꇤ⁐ꃳ 三
마지막으로 다음 팀원이 만든 코드 엄청난 오류를 뿜어내던 filternav였다.
import React from "react";
import { aesopLogoPath } from "../config";
import * as productList_API from "../config";
import { withRouter } from "react-router-dom";
import "./Productfilternav.scss";
class Productfilternav extends React.Component {
state = {
category : []
};
clickHandler = (id) => {
this.props.history.push(`${id}`)
}
componentDidMount() {
fetch(`http://15.164.220.49:8080/board/1/1`)
.then((res) => res.json())
.then((res) => this.setState({ category: res.data.category }));
}
render() {
const { category } = this.state;
const path = aesopLogoPath;
return (
<section className="Productfilternav">
<div className="logoContainer">
<svg
aria-labelledby="ca6be9cd-5480-4526-b1d6-17bb54dbe2b2"
class="Icon NavLogo-icon"
role="img"
viewBox="0 0 489.7 154.3"
>
<title id="ca6be9cd-5480-4526-b1d6-17bb54dbe2b2">Aesop logo</title>
<g>
<path class="NavLogo-icon--path" d={path.a}></path>
<path class="NavLogo-icon--path" d={path.e}></path>
<path class="NavLogo-icon--path" d={path.s}></path>
<path class="NavLogo-icon--path" d={path.o}></path>
<path class="NavLogo-icon--path" d={path.p}></path>
<rect
x="131.8"
y="13.5"
class="NavLogo-icon--path"
width="44.3"
height="5"
></rect>
<path class="NavLogo-icon--path" d={path.corp}></path>
</g>
</svg>
</div>
<section className="productListContainer">
<div className="productTitleContainer">
<div className="productTitleBox">
<h1 className="productTitle">스킨</h1>
</div>
</div>
<div className="productFilterContainer">
<div className="productFilterBox">
<ul className="productFilterNav">
<li>
<span className="start">모든 스킨</span>
</li>
{category &&
category.map((el) => {
return (
<li key={el.id} onClick={() =>this.clickHandler(el.id)}>
<span>{el.name}</span>
</li>
);
})}
</ul>
<div className="productFilter">
<button className="filterBtn">
<div className="filterBox">
<span>필터</span>
<svg class="btnIcon" role="img" viewBox="0 0 50 50">
<g>
<polygon points="25,31.3 4.2,10.5 0.1,14.6 25,39.5 50,14.6 45.9,10.5 "></polygon>
</g>
</svg>
</div>
</button>
</div>
</div>
</div>
</section>
</section>
);
}
}
export default withRouter(Productfilternav);
왜 남이 만든 코드를 가져왔나? 싶기도 할 것이다.
clickHandler = (id) => {
this.props.history.push(`${id}`)
}
componentDidMount() {
fetch(`http://15.164.220.49:8080/board/1/1`)
.then((res) => res.json())
.then((res) => this.setState({ category: res.data.category }));
}
여기서도 내가 만든 부분이 있기에 그렇다.
처음에는 componentDidMount가 해당 코드에 없었고, 이를 내꺼에서 부르자 출력이 되지 않는 문제를 발견.
이전에도 적었다싶이 해당작업을 한 팀원을 불러 같이 해결.
상위 컴포넌트에서 props로 인자를 받는데, 내가 이것만 부르니 불러오지 못하던 것이 문제였다.
이를 추가하여 간단히 해결.
다음으로 위의 코드처럼 클릭이벤트에 id만 받아오면 다른 곳에서 해당코드가 경로 불러오기가 작동되지 않았다.
오직 나의 것에서만 불러오기가 가능했는데, 이를 maplist의 내용을 추가해줌에 따라 가볍게 해결되었다.
clickHandler = (id) => {
this.props.history.push(`/Maplist/${id}`)
}
다른 곳에서 만든 태그가 그냥 수월하게 작동되는 것보니 협업할만하더라 싶기도하고, 의외로 오류잡기가 어려운게 힘들었다.
해당 1개의 카테고리를 클릭했때, 함수에 인자를 주는 것을 몰랐는데,
이것을 해결할때 arrow 펑션으로 주는 것을 배웠다.
해당 이벤트가 발생하면 el.id를 인자로 받아와 해당 글씨를 클릭하면 작동되게 하는...
{category &&
category.map((el) => {
return (
<li key={el.id} onClick={() =>this.clickHandler(el.id)}>
<span>{el.name}</span>
</li>
);
})}
</ul>
해당 기능이 구현되는 모습니다.
클랜저 등을 누르면 내가 만든 상세 카테고리로 넘어간다.
마지막으로 모두 열심히 만들어 붙여 본 결과물.
안타깝게도 장바구니에서 물건을 추가하면 백엔드 api에서 물품의 총합값을 계산해서
출력해주는 부분이 완성되지 못하여 발표때에도 해당 부분을 올리지 못했다.
결국은 해결되지 않은 부분이라 아쉽지만, 꼭 다음에는 구현하리라는 생각을 하게되었다.
프론트엔드가 빨리했으면 백엔드에서도 해당 기능을 구현할 수 있지 않았까라는 생각을 하게 되기도 하였고,
나름 대화를 많이했다고 생각하는데, 우리가 필요한 기능을 구현하는데 있어 아직 모르는 부분이 많아
백엔드와 정확하게 많은 통신이 이루어지지 않은 점이 아쉬웠다.
'코딩 > 위코드 코딩학습' 카테고리의 다른 글
[위코드] TIL(Today I am learned) -22(2차프로젝트 시작/트립어드바이저) (0) | 2020.08.06 |
---|---|
위코드 10기 1차 프로젝트 클론코딩 후기.(이솝/aesop) (0) | 2020.08.03 |
[위코드] TIL(Today I am learned) -20 (0) | 2020.07.30 |
[위코드] TIL(Today I am learned) -19 (0) | 2020.07.29 |
[위코드] TIL(Today I am learned) -18(onMouse이벤트/computed property named) (0) | 2020.07.28 |