Skip to content

Spring at Rowem

Backend development while at Rowem Inc.

backend Development

  • Java

  • API development for SBS inkigayo voting platform

  • SDKs used : AWS SNS, AWS S3
  • APIs used : Payletter, Apple login
  • Scheduling (Spring Framework) for Batch Job

    • Code Optimization (e.g. for-loop)
    • Refactoring for readability and reusability of codes
    • Junit unit test
  • Git

    • Pull Request, Code Review, Merge
    • Branch : 'main < test < develop'

Code Optimization

/**
 * Limit fetch size (query limit count) for each sql query execution
 *  so that it does not put stress on database
 */
final int fetchSize = AppConstants.DEFAULT_FETCH_SIZE;

// Total list count
final int listCount = batchApiMapper.getVoteListCnt();

// Limit for iteration
final int limit = listCount + fetchSize;
BatchVO param = new BatchVO();

for (int ii = 0; ii < limit;) {
    param.setPage_index(ii);
    param.setRecord_count_per_page(fetchSize);

    List<BatchVoteVO> mainList = batchApiMapper.getVoteInfoList(param);
    for (BatchPlayvoteVO m : mainList) {
        batchApiMapper.updateVote(m);
    }
    ii += fetchSize;
}
// ...

Refactoring

/**
 * API for executing voting process includes updating and retrieving from database
 *  refactoring helped to improve readability
 */
@Override
@Transactional
public ResultVO insertVote(VoteInsertVO param) throws Exception {

    // Get vote info including target candidate that a user is requesting
    VoteInsertVO vote = getVoteUserInfo(param);

    // Calculate total vote points
    //  internal point system include point conversion!
    setTotalVotePointInfo(vote);

    // Operation for SELECT FOR UPDATE
    //  retrieves data for update (update is locked during Lock Wait Time)
    VoteDetailUserVO detailUser = selectVoteDetailUserForUpdate(vote);

    // Validate remaining user points
    validateVoteInfo(vote, vote.getVote_use_count());

    // Insert vote result data : tran_no, hist_seq
    insertVoteHistory(vote);

    // Process point deduction based on the user request which includes use point type
    String voteUsePointType = param.getVote_use_point_type();
    if (VoteUsePointType.SILVER.equals(voteUsePointType)) {
        useSilverPoint(vote);
        useGoldPoint(vote);
    }
    else if (VoteUsePointType.GOLD.equals(voteUsePointType)) {
        useGoldPoint(vote);
    }

    // Insert another vote history data
    //  which is used for statistical purpose
    //  e.g. for future vote candidate recommendation or viewing voting history
    insertVoteHist(vote);

    // Update Vote aggregate data
    detailUser.setVote_use_point(vote.getVote_use_point());
    detailUser.setVote_count(vote.getVote_use_count());
    updateVoteDetailUser(detailUser);

    // Return remaining vote count for a user after the transaction is complete
    //  in case of a vote platform that restrict the vote counts for each user
    if (!VoteLimitCountType.NOLIMIT.equals(vote.getVote_limit_cnt_type())) {
        int remain_cnt = selectRemainCnt(vote);
        ResultDoPlayVoteVO result = new ResultDoPlayVoteVO();
        result.setVote_remain_cnt(remain_cnt);
        result.setCode(ErrorCode.SUCCESS.getCode());
        result.setMessage(getMessage(ErrorCode.SUCCESS.getKey()));
        return result;
    } else {
        ResultVO result = new ResultVO();
        result.setCode(ErrorCode.SUCCESS.getCode());
        result.setMessage(getMessage(ErrorCode.SUCCESS.getKey()));
        return result;
    }
}



Unit test

package com.oo.ooo.home;

import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.rowem.passicon.SpringTestBase;


/**
 * OO GET Test
 *
 * @author junho
 *
 */
public class DataApiTest extends SpringTestBase {

    @Autowired
    private HomeApiService homeApiService;

    @Autowired
    private HomeApiMapper homeApiMapper;

    @Autowired
    private DataService dataService;

    /**
     * Delete data
     *
     * @throws Exception
     */
    private void deleteData() throws Exception {

        DataVO data = null;
        do {
            MyObj param = new MyObj();
            param.setLogin_id("ooo")

            // Find data to be deleted
            data = dataApiMapper.selectMyData(param);
            if (data != null) {
                ResultVO result = dataService.deleteData(data);
                assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode());
            }
        } while(star_info != null);
    }

    /**
     * Insert data
     *
     * @throws Exception
     */
    private void insertData() throws Exception {

        MyObj s = new MyObj();
        s.setLogin_id("ooo")
        ResultVO res = dataService.insertMyData(s);
        assertEquals(ErrorCode.SUCCESS.getCode(), res.getCode());
    }

    /**
     * 0-1 Home API test
     *
     * CASE1. mystar X, vote hist X, backoffice > recommend star list 0 (all deactivated: stat=2)
     *
     * @throws Exception
     */
    @Test
    public void testGetStarInfo_1() throws Exception {
        String login_id = "ooo";
        DataParamVO param = new DataParamVO();
        param.setLogin_id(login_id);

        // mystar X
        deleteData();

        // vote hist X
        List<StarInfoVO> recom_votehist_list = homeApiMapper.selectStarVoteLogList(param);
        assertEquals(0, recom_votehist_list.size());

        DataResultVO result = (DataResultVO) homeApiService.getStarInfo(param);
        assertEquals(null, result.getStar_info());
        assertEquals(0, result.getRecom_votehist_list().size());
        assertEquals(null, result.getRefresh_size());
        assertEquals(0, result.getRecom_admin_list().size());
        assertEquals(null, result.getVote_playvote_list());
        assertEquals(null, result.getVote_indiv_rank());
        assertEquals(null, result.getVote_battle_rank());
    }

    /**
     * case2. mystar X, vote history X, backoffice > recommend star list at least 1 (activated  stat=1)
     *
     * @throws Exception
     */
    @Test
    public void testGetStarInfo_2() throws Exception {
        String login_id = "ooo";
        DataParamVO param = new DataParamVO();
        param.setLogin_id(login_id);

        // mystar X
        deleteData();

        // vote history X
        List<StarInfoVO> recom_votehist_list = homeApiMapper.selectStarVoteLogList(param);
        assertEquals(0, recom_votehist_list.size());

        DataResultVO result = (DataResultVO) homeApiService.getStarInfo(param);
        assertEquals(null, result.getStar_info());
        assertEquals(0, result.getRecom_votehist_list().size());
        assertEquals(null, result.getRefresh_size());
        assertEquals(Boolean.TRUE, result.getRecom_admin_list().size() > 0);
        assertEquals(null, result.getVote_playvote_list());
        assertEquals(null, result.getVote_indiv_rank());
        assertEquals(null, result.getVote_battle_rank());
    }
}
package com.oo.ooo;

import javax.servlet.ServletContext;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

/**
 * SpringTestBase class
 *
 * @author junho
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml",
        "file:src/main/webapp/WEB-INF/spring/root-context.xml",
        "file:src/test/resources/spring/context-test-mapper.xml", })
@WebAppConfiguration
@ActiveProfiles("local")
public class SpringTestBase {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    protected WebApplicationContext wac;

    protected MockMvc mockMvc;

    protected ObjectMapper objectMapper;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
        objectMapper = new ObjectMapper();
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        objectMapper.setDefaultPrettyPrinter(new DefaultPrettyPrinter());
    }

    /**
     * print object as JSON
     *
     * @param name
     * @param value
     */
    protected void printJson(String name, Object value) {
        try {
            logger.info("{}:\n{}", name, objectMapper.writeValueAsString(value));
        } catch (JsonProcessingException e) {
            logger.error("error :", e);
        }
    }

    @Test
    public void isWebAppContextLoadingProperly() {
        ServletContext servletContext = wac.getServletContext();
        Assert.assertNotNull(servletContext);
        Assert.assertTrue(servletContext instanceof MockServletContext);
    }
}