- Service Introduction
- UI/UX Design
- Frontend : Vue
- RESTful API
- Kakao Platform API
- Backend :Spring Boot
- JWT
- DB : MySql
Happy House, which is an online real estate agent platform, was created by two young and talented Korean developers in December 2020. At the target of young generation who wants to be cool and hip, this has been made to provide them the most useful experience in looking for various kinds of studios, apartments that they would live in. For that, expectation and anxiety that come from a new living place were set as main keywords that we should deal with in our website. In other words, we wanted them to be more hopeful and less nervous by our numerous services. Because it is a prototype, it will not get updated unless there are major issues. If you have any questions, please shoot an email to deakse2@gmail.com.
We set our main color to light pink for better visual convenience. So, tens of logo designs fitted our brans strategy were suggested with the help of Tailer Brands Studio and the current design was finally chosen throguh delicate reviews. Along that, the main page was also changed to the amazing illustration of Gala Poliakova, which looks more natural than just a google-style simple design. She is an great illustrator working for a meditation app company. If you love it, I am 100% sure that her other works would also catch your eyes. Check them out right now on the link. -> https://www.artstation.com/galapoliakova
Static image is not cool any more to attract the young clients' attention. We added some fun interactions for them to think it is so cool that they want to keep using. Among them, the one highlighted here is to show the weather of an user's current location. For example, when it is raining in the place where a user lives and he/she begins typing something on a search bar, it starts raining on our main page, too. For real! Here's how it looks. I got a referrence in CodePen and re-created it in VueJs. Please see the link attached if interested. -> https://codepen.io/arickle/pen/XKjMZY
We used the Vue framwork for frontend. Before that, we made web pages to .jsp, that was totally undesiable since all the html and css codes were mingled with java codes. After the installation of Vue CLI, however, we could make our messy files and codes well-organized.
We used RESTful API through Vue Axios to exchange JSON type data. Mainly, client requested to server to call a list of houses located in the address that users typed on a search bar. Here's the one sample code we used.
Axios
.get("http://localhost:8000/happyhouse/heart/search")
.then((response) => {
this.items = response.data;
// change data to charts
let obj = new Object(),
labels = new Array(),
data = new Array();
response.data.forEach((elem) => {
labels.push(elem['dong']);
data.push(elem['cnt']);
})
obj.labels = labels;
obj.data = data;
this.chart = obj;
})
.catch(() =>{
alert("error occured");
})
In order to show users where the places are located, we used Kakao Map API, which is way convenient in terms of customizing and pricing than Google Map API. It provides various types of libraries for users to be able to add directly on the map rendered. They can add/remove markers and move map by drag to find other area. Calling the Vue instace in the event listener was very sticky since whenever 'this' keyword was called on console, it indicated the event listener, not the instance, due to the asynchronous communication. For that, I used some tricks; that : this, Promise
let latlng = null;
let geocoder = new kakao.maps.services.Geocoder();
let that = this; // Super Important!!!!
kakao.maps.event.addListener(map, 'dragend', function() {
latlng = map.getCenter();
const addressSearch = latlng => {
return new Promise((resolve, reject) => {
geocoder.coord2RegionCode(latlng.getLng(), latlng.getLat(), function(result, status) {
if (status === kakao.maps.services.Status.OK) {
resolve(result);
} else reject(status);
});
})
}
(async () => {
try {
const result = await addressSearch(latlng);
for (let index = 0; index < result.length; index++) {
if (result[index].region_type === 'H') {
let si = result[index].region_1depth_name,
gugun = result[index].region_2depth_name,
dong = result[index].region_3depth_name;
let arr = dong.split("");
for (let j = 0; j < arr.length; j++) {
if (isNaN(arr[j])) continue;
arr.splice(j)
dong = arr.join('')
}
let newAddress = si+" "+gugun+" "+dong;
that.keyword = newAddress;
that.getKeyword("");
break;
}
}
} catch(e) {
console.log(e);
}
})();
})
Spring Boot was used for its simple usage and multiple advantages such as autoconfiguration, starter POMs. To make a Spring MVC-based REST application, Spring framework itself is good enough but there are lots of xmls for matching dependencies and configuration. Spring Boot takes away all these pains and lets you write the code that matters, which means that Java development with Spring is very simplified. Among various functions, we would like to show you several key things that must be implemented in a server side to work as a webpage.
Log in/out functions are requisites to make other services and if an user is using our service, his or her id and password must be stored somewhere to remain logged in even when he or she move to a different page. For that, the basic method is to use Cookie and Session. It's way easier to implement and more straightforward than other methods. However, it has a crucial problem called CORS error, which usually occurs when the origin of client is different with that of server. What does it mean? it means that you violated the same-origin policy that restricts how a document or script loaded from one origin can interact with a resource from another origin. If you want to make a Single-Page Application(SPA), you will get the error inevitably because client and server has its own domain respectively. Moreover, you need to consider mobile environments. That's why we used the JWT. Unlike using cookie and session, this token-based authentification does not store user's data in neither client nor server. The token created becomes the data as itself and is used in any environment due to the JSON-format communication. Then, how is it created at first? Here's the sample code we wrote.
@RequestMapping(value = "/login", method = RequestMethod.POST)
private ResponseEntity<Map<String, Object>> login(@RequestBody MemberDto member) throws ServletException, IOException {
Map<String, Object> resultMap = new HashMap<>();
HttpStatus status = null;
try {
MemberDto loginUser = memberService.login(member);
if(loginUser != null) {
String token = jwtService.create(loginUser);
logger.trace("login token info : {}", token);
resultMap.put("auth-token", token);
resultMap.put("id", loginUser.getId());
status = HttpStatus.ACCEPTED;
}else {
resultMap.put("message", "로그인 실패");
status = HttpStatus.ACCEPTED;
}
}catch(Exception e) {
logger.error("Login failed : {}", e);
resultMap.put("message", e.getMessage());
status = HttpStatus.INTERNAL_SERVER_ERROR;
}
return new ResponseEntity<Map<String, Object>>(resultMap, status);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssafy.happyhouse.model.mapper.HouseMapMapper">
<select id="getDongInGugun" resultType="HashMap" parameterType="string">
<![CDATA[SELECT distinct dong, dongcode FROM dongcode WHERE substring(dongcode, 1, 5) = #{gugun} ORDER BY dong]]>
</select>
<select id="searchApt" resultType="houseinfo" parameterType="String">
<![CDATA[
select no, dong, code, info.AptName, jibun, lat, lng, price, area, date
from
(select AptName, dealAmount price, area, str_to_date(concat(dealYear,"-",dealMonth,"-",dealDay), '%Y-%m-%d') as date
from housedeal group by AptName order by date desc) as info
inner join
(select no, dong, code, AptName, jibun, lat, lng
from houseinfo info
where info.code in (select base.code from baseaddress base where concat(city,' ',gugun,' ',dong) like #{ address })) as deal
on info.AptName = deal.AptName;
]]>
</select>
<insert id="addLike" parameterType="houselike">
<![CDATA[insert into interestarea (id, apt_name, price, area, last_update) values (#{id}, #{apt_name}, #{price}, #{area}, #{last_update});]]>
</insert>
<select id="checkPrice" resultType="housedeal" parameterType="String">
<![CDATA[select no, dong, AptName apt_name, dealAmount deal_amount, date_format(str_to_date(concat(dealYear,'-',dealMonth,'-',dealDay), '%Y-%m-%d'), '%m-%d') as deal_date
from housedeal where AptName = #{AptName} order by deal_date;]]>
</select>
</mapper>
npm install
npm run serve
npm run build
npm run lint