찌읏 홈페이지 프로젝트 후기를 작성해보자.
한 달동안 현업에서 생활해보니 위코드에서 공부했을 때와는 많이 달랐다. 위코드에서 프로젝트를 했을 때에는 딱딱 정해진대로 물흐르듯이 흘러가는 느낌이었다면, 스타트업은 정해진게 없고, 언제든지 계획이 바뀌는구나를 느꼈다. 정해진 계획이 없고, 이것저것 바껴서 처음에는 당황스러웠지만 이게 스타트업이 돌아가는 모습이겠구나 적응해야겠다는 생각이 들었다.
내가 맡은 곳은 자사홈페이지 '서비스 소개'부분이었다. 뷰를 사용해보지 않았다고 해서 그런지 회사에서는 단순한 페이지 하나를 맡기셨다. 뷰를 사용하기 위한 구성이나 뷰에 익숙해지는 시간을 가졌다. 따로 페이지를 더 만들라고 하시지는 않았지만, 뷰를 이용해서 다른 페이지도 만들어보고싶어서 로그인,회원가입을 페이지를 만들었다.
우연히 LoginCritter라는 프로젝트를 알게되었는데 그 기능을 로그인페이지에 꼭 적용해보고싶었다.
LoginCritter란 로그인 정보를 인풋창에 입력하는 동안 캐릭터가 커서를 따라 눈길을 주는 것을 말한다.
현업에서는 대부분의 데이터를 api통신을 받아온다고 한다. 리액트에서는 fetch함수를 이용해서 데이터를 받아왔었는데 뷰에서는 axios함수를 통해 데이터를 받아온다고 한다.
// Login.vue
<template>
<form class="loginForm">
<svg id="figure" viewBox="0 0 120 120">
<path
d="M0,150 C0,65 120,65 120,150"
fill="
#FAF4E8"
stroke="black"
stroke-width="2"
/>
<circle
cx="60"
cy="60"
r="40"
fill="#FAF4E8"
stroke="#000"
stroke-width="2"
/>
<g class="eyes">
<circle :cx="leftEye.x" :cy="leftEye.y" r="5" fill="#000" />
<circle :cx="rightEye.x" :cy="rightEye.y" r="5" fill="#000" />
</g>
</svg>
<input
v-model="email"
type="text"
placeholder="email"
@input="adjustLocation"
@keydown.tab="checkClicked"
/>
<span class="errorMsg">{{ emailErrMsg }}</span>
<input
v-model="password"
type="password"
placeholder="Password"
@click="[checkClicked(), stopEvent()]"
/>
<span class="errorMsg">{{ passwordErrMsg }}</span>
<span class="forgetPW">비밀번호를 잊으셨나요?</span>
<button>로그인</button>
</form>
</template>
<script>
export default {
props: ["propsData"],
data() {
return {
email: "",
emailErrMsg: "",
password: "",
passwordErrMsg: "",
leftEye: { x: 44, y: 55 },
rightEye: { x: 76, y: 55 },
isClicked: false
};
},
watch: {
email: function() {
const isMail = /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z]+$/;
if (!isMail.test(this.email) && this.email.length) {
this.emailErrMsg = "올바른 이메일이 아닙니다.";
} else if (!this.email.length) {
this.emailErrMsg = "이메일을 입력해주세요.";
} else {
this.emailErrMsg = "";
}
},
password: function() {
if (!this.password.length) {
this.passwordErrMsg = "비밀번호를 입력해주세요.";
} else if (this.password.length < 8) {
this.passwordErrMsg = "8자이상 입력해주세요.";
} else {
this.passwordErrMsg = "";
}
}
},
methods: {
adjustLocation() {
const { length } = this.email;
if (length === 0) {
this.leftEye.x = 44;
this.rightEye.x = 76;
this.leftEye.y = 55;
this.rightEye.y = 55;
} else if (length < 20) {
this.leftEye.x = 34 + length / 2;
this.rightEye.x = 66 + length / 2;
this.leftEye.y = 60 + length / 2;
this.rightEye.y = 60 + length / 2;
} else if (19 < length && 34 >= length) {
console.log(length);
this.leftEye.x = 36 + length / 2;
this.rightEye.x = 68 + length / 2;
this.leftEye.y = 80 - length / 2;
this.rightEye.y = 80 - length / 2;
} else if (length > 34) {
this.leftEye.x = 53;
this.rightEye.x = 85;
this.leftEye.y = 63;
this.rightEye.y = 63;
}
},
checkClicked() {
this.isClicked = true;
this.leftEye.x = 44;
this.rightEye.x = 76;
this.leftEye.y = 38;
this.rightEye.y = 38;
},
resetLocation() {
this.leftEye.x = 44;
this.rightEye.x = 76;
this.leftEye.y = 55;
this.rightEye.y = 55;
},
stopEvent(event) {
event.stopPropagation();
}
}
};
</script>
//Register.vue
export default {
components: { Login, Signup },
data() {
return {
activeId: 1
};
},
methods: {
clickTab(id) {
this.activeId = id;
},
detectOutsideClicked() {
this.$refs.login.resetLocation(); //자식 컴포넌트의 함수를 호출
},
}
};
ref속성: 자식 컴포넌트(Login.vue)의 resetLocation함수를 부모컴포넌트의 전체를 감싸주는 div에 호출하기 위해서 ref를 사용했다. ref속성을 이용해 자식 요소에 레퍼렌스 ID를 할당하여 연결해주어 사용한다.
event.stopPropagation 함수: 이벤트의 전파를 막기 위해 사용하는 함수이다. 상위태그에 정의한 detectOutsideClicked함수가 버블링의 영향으로 하위태그에도 영향을 미치게 된다. 비밀번호 인풋창에는 이 함수의 이벤트를 막아야하기때문에 input에 stopPropagation을 사용해주었다.
유효성검사
리액트에서 한번 구현한 기능이기때문에 비슷할거라고 생각했는데 은근 오래걸린 것 같다. 변하는 값을 캐치해야기때문에 computed와 watch 중 어느 것을 사용해야하나 고민이 들었다.
watch가 특정 프로퍼티의 변경시점에 특정 액션을 취하고자 할때 적합하다고 해서 watch를 사용했다.