Generate loopable sine wave cycle for given frequencies
Your clicks are coming from two sources. The wavetable and the hardware.

If you are looking to create a simple little instrument that produces tones by playing back a wavetable, then there is no way to fix the size of the "playback window" to reproduce tones without clicks.

The frequencies that compose the chromatic scale follow a geometric progression that achieves doubling of the frequency in 12 steps. The frequency for each tone is produced by multiplying the previous tone by something like 1.05. That factor is worked out by: Let's take $A_F = 440Hz$ and its octave at $A_O = 880Hz$. Each one of the tones is $A_{n+1} = A_n * r$, so $A_O = A_F \cdot r^{12}$. But we know that $A_O = A_F \cdot 2$. Therefore, $r^{12} = 2$ and $r \approx 1.059463094359$

So, you might fit $A_F,A_O$ neatly in a wavetable, but $A_F \sharp$ now needs $\approx 1.0594$ more samples to fit in neatly. Not only there is no integer there but even if you could reproduce the tones in neat windows you would get problems with the tempo, the duration of each note in a musical piece, because $A_F$ is shorter than $A_F \sharp$.

The "classic" way around this is to setup a high resolution wavetable of a single sinusoid which is then read back at "different speeds", with interpolation. So, $A_F$ is read back at $r^0 = 1$ speed (and practically no interpolation) but $A_F \sharp$ is read back at $r^{1} \approx 1.0594$ speed and a bit of interpolation when the "needle" has to land between known samples.

BUT!, this has nothing to do with your output stream. In other words, you have to set-up (or adopt) a stream system where samples are continuously being pushed to the sound card and you have a "global" sense of time.

Once upon a time, you did this by setting up three pointers (that's in C, C++) to three buffers. Let's call them $H,D,L$. You set up the sound card to record in $H$ and playback $L$ and while that was going on, your main code was processing buffer $D$. When the sound card called its callback to signify that the recording ended, you would "circularly switch" the buffers around so that they now pointed to $L \rightarrow D, D \rightarrow H, H \rightarrow silence$ and re-set up the sound card to do the same job while you were now processing the $D$ which is now your freshly recorded $H$. Those buffers had a length, say for instance 1000 samples and the task then was becoming to find a sweet spot that balanced length of buffer with responsiveness.

But as you can see, in this system, samples are being pushed out to the sound card continuously in blocks of $bufferLength$ samples and you don't really care if what is being played back fits the buffer. All that you do is iterate your models (e.g. the wavetable), produced a buffer of samples, freeze the models, playback that buffer and move again from the top.

Now, obviously, the above includes real time input from audio sources too. If all you are doing is a tiny little synth, you could have two buffers and switch around between them, working on one while the other is being played back.

But even if you did this with some off the shelf cheap sound card in the market, you still got clicks although, you have worked so hard to perfectly align the zeroes between blocks. This is because of something called latency.

In other words, just because your program called some Operating System Application Programming Interface function that said "Record NOW!" (or "Play NOW!), it doesn't mean that this command will propagate through the driver and eventually the audio hardware and recording will start NOW. Recording (and playback) will commence some $NOW! + latency$ units of time later and in off the shelf sound cards, this used to be an awful time later. So awful that you could hear the silence between calls.

So, the solution there was to move over to something like ASIO which eventually packaged this whole workflow into a complete solution all the way from the software calls to the hardware.

As an example, an 8-track external sound card can do 48kHz Stereo playback / rec over buffers of 256 samples (with absolutely no clicks). This means, that sound is processed in blocks that last approximately 5ms. The human ear begins resolving sounds as distinct when they are apart for more than 40ms, so this 5ms is really safe. It means that we can plug an electric instrument in the sound card and process it, in real time like it went through an effects box and the performer will not be noticing any difference between what they play and what they hear.

If you tried to make $H,D,L$ the size of 256 and try to do the same with an off the shelf (relatively popular) sound card, first of all you would g/et/ /an/awf/ul/am//ount/of/cli/k/s// (one every 5ms) and secondly, the driver and operating system would be so much overwhelmed by the amount of function calls that soon you would be greeted with Seg Faults, blue screens, system freeze, reprepreprepreprepreprepreprepreprereprepreprepreprepreprepeated waves out of synsynsynsynsynsynsynsynsynsynreprepresync and other amusing side effects.

So, bottom line, keep working on the DSP side of your synth but make sure that your sound hardware is up to the task as well if you really want to get rid of the clicks.

Hope this helps.